From 2e3e5f67bc88c94b00b86baf987b36bdba754632 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Tue, 22 Aug 2017 15:16:07 +0200 Subject: [PATCH] DATAJPA-1168 - Introduce usage of nullable annotations for API validation. Mark all packages with Spring Frameworks @NonNullApi. Add Spring's @Nullable to methods, parameters and fields that take or produce null values. Adapt using code to make sure the IDE can evaluate the null flow properly. Fix Javadoc in places where an invalid null handling policy was advertised. Strengthen null requirements for types that expose null-instances. Introduce methods returning non-null values for required values that are checked for existence prior to retrieval. Add generic types to IsAttributeNode factory methods. Original Pull Request: #210 --- .../QueryByExamplePredicateBuilder.java | 38 +++++---- .../data/jpa/convert/package-info.java | 7 ++ .../jpa/convert/threeten/package-info.java | 7 ++ .../jpa/convert/threetenbp/package-info.java | 7 ++ .../data/jpa/domain/AbstractAuditable.java | 10 ++- .../data/jpa/domain/AbstractPersistable.java | 8 +- .../data/jpa/domain/JpaSort.java | 19 +++-- .../data/jpa/domain/Specification.java | 4 + .../data/jpa/domain/Specifications.java | 21 +++-- .../data/jpa/domain/package-info.java | 2 + .../support/AuditingEntityListener.java | 16 +++- .../data/jpa/domain/support/package-info.java | 2 + .../mapping/JpaMetamodelMappingContext.java | 8 ++ .../jpa/mapping/JpaPersistentEntityImpl.java | 3 +- .../mapping/JpaPersistentPropertyImpl.java | 10 ++- .../data/jpa/mapping/package-info.java | 2 + .../data/jpa/provider/HibernateUtils.java | 21 +++-- .../jpa/provider/PersistenceProvider.java | 32 +++++-- .../data/jpa/provider/ProxyIdAccessor.java | 6 +- .../data/jpa/provider/QueryExtractor.java | 6 +- .../data/jpa/provider/package-info.java | 7 ++ .../repository/JpaSpecificationExecutor.java | 15 ++-- .../jpa/repository/cdi/JpaRepositoryBean.java | 2 +- .../cdi/JpaRepositoryExtension.java | 2 +- .../data/jpa/repository/cdi/package-info.java | 2 + ...JpaMetamodelMappingContextFactoryBean.java | 31 +++++-- .../config/JpaRepositoryConfigExtension.java | 24 +++--- .../jpa/repository/config/package-info.java | 2 + .../data/jpa/repository/package-info.java | 2 + .../repository/query/AbstractJpaQuery.java | 31 ++++--- .../query/DefaultJpaEntityMetadata.java | 11 ++- .../data/jpa/repository/query/Jpa21Utils.java | 15 +++- .../query/JpaCountQueryCreator.java | 4 +- .../jpa/repository/query/JpaEntityGraph.java | 6 +- .../jpa/repository/query/JpaParameters.java | 33 ++++++-- .../jpa/repository/query/JpaQueryCreator.java | 26 +++--- .../repository/query/JpaQueryExecution.java | 12 +-- .../jpa/repository/query/JpaQueryFactory.java | 9 +- .../query/JpaQueryLookupStrategy.java | 26 +++--- .../jpa/repository/query/JpaQueryMethod.java | 39 +++++++-- .../repository/query/JpaResultConverters.java | 11 +-- .../data/jpa/repository/query/NamedQuery.java | 10 ++- .../jpa/repository/query/ParameterBinder.java | 2 +- .../query/ParameterBinderFactory.java | 8 +- .../query/ParameterMetadataProvider.java | 12 ++- .../repository/query/PartTreeJpaQuery.java | 12 ++- .../query/QueryParameterSetter.java | 8 +- .../query/QueryParameterSetterFactory.java | 64 ++++++++------ .../data/jpa/repository/query/QueryUtils.java | 23 ++--- .../jpa/repository/query/SimpleJpaQuery.java | 7 +- .../query/StoredProcedureAttributeSource.java | 5 +- .../query/StoredProcedureAttributes.java | 8 +- .../query/StoredProcedureJpaQuery.java | 5 +- .../jpa/repository/query/StringQuery.java | 74 +++++++++++++---- .../jpa/repository/query/package-info.java | 2 + .../support/CrudMethodMetadata.java | 3 + .../CrudMethodMetadataPostProcessor.java | 15 ++-- .../support/JpaEntityInformation.java | 25 +++++- .../JpaMetamodelEntityInformation.java | 46 ++++++---- .../JpaPersistableEntityInformation.java | 3 + .../support/JpaRepositoryFactory.java | 3 +- .../support/JpaRepositoryFactoryBean.java | 11 ++- .../data/jpa/repository/support/Querydsl.java | 2 +- .../support/QuerydslJpaRepository.java | 13 ++- .../support/QuerydslRepositorySupport.java | 33 ++++++-- .../support/SimpleJpaRepository.java | 83 ++++++++++--------- .../jpa/repository/support/package-info.java | 2 + ...hScanningPersistenceUnitPostProcessor.java | 9 +- .../MergingPersistenceUnitManager.java | 4 +- .../data/jpa/support/package-info.java | 2 + .../data/jpa/util/BeanDefinitionUtils.java | 9 +- .../data/jpa/util/JpaMetamodel.java | 27 +++--- .../data/jpa/util/package-info.java | 7 ++ .../data/jpa/domain/JpaSortTests.java | 5 +- .../data/jpa/domain/sample/AuditableUser.java | 15 ++-- .../support/AuditingEntityListenerTests.java | 3 +- ...lipseLinkNamespaceUserRepositoryTests.java | 10 +-- .../jpa/repository/UserRepositoryTests.java | 31 +++---- .../CustomRepositoryFactoryConfigTests.java | 9 +- .../jpa/repository/custom/package-info.java | 4 + .../jpa/repository/query/Jpa21UtilsTests.java | 7 +- .../query/StringQueryUnitTests.java | 6 +- .../JpaRepositoryFactoryBeanUnitTests.java | 1 - .../data/jpa/util/IsAttributeNode.java | 22 +++-- 84 files changed, 790 insertions(+), 399 deletions(-) create mode 100644 src/main/java/org/springframework/data/jpa/convert/package-info.java create mode 100644 src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java create mode 100644 src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java create mode 100644 src/main/java/org/springframework/data/jpa/provider/package-info.java create mode 100644 src/main/java/org/springframework/data/jpa/util/package-info.java create mode 100644 src/test/java/org/springframework/data/jpa/repository/custom/package-info.java diff --git a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java b/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java index 32bfd5d28..5008809f7 100644 --- a/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java +++ b/src/main/java/org/springframework/data/jpa/convert/QueryByExamplePredicateBuilder.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,6 +19,7 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; import javax.persistence.criteria.CriteriaBuilder; @@ -35,8 +36,10 @@ import javax.persistence.metamodel.SingularAttribute; import org.springframework.dao.InvalidDataAccessApiUsageException; import org.springframework.data.domain.Example; import org.springframework.data.domain.ExampleMatcher; +import org.springframework.data.domain.ExampleMatcher.PropertyValueTransformer; import org.springframework.data.repository.core.support.ExampleMatcherAccessor; import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; +import org.springframework.lang.Nullable; import org.springframework.orm.jpa.JpaSystemException; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -60,7 +63,7 @@ public class QueryByExamplePredicateBuilder { private static final Set ASSOCIATION_TYPES; static { - ASSOCIATION_TYPES = new HashSet(Arrays.asList(PersistentAttributeType.MANY_TO_MANY, + ASSOCIATION_TYPES = new HashSet<>(Arrays.asList(PersistentAttributeType.MANY_TO_MANY, PersistentAttributeType.MANY_TO_ONE, PersistentAttributeType.ONE_TO_MANY, PersistentAttributeType.ONE_TO_ONE)); } @@ -100,7 +103,7 @@ public class QueryByExamplePredicateBuilder { static List getPredicates(String path, CriteriaBuilder cb, Path from, ManagedType type, Object value, Class probeType, ExampleMatcherAccessor exampleAccessor, PathNode currentNode) { - List predicates = new ArrayList(); + List predicates = new ArrayList<>(); DirectFieldAccessFallbackBeanWrapper beanWrapper = new DirectFieldAccessFallbackBeanWrapper(value); for (SingularAttribute attribute : type.getSingularAttributes()) { @@ -111,10 +114,11 @@ public class QueryByExamplePredicateBuilder { continue; } - Object attributeValue = exampleAccessor.getValueTransformerForPath(currentPath) - .convert(beanWrapper.getPropertyValue(attribute.getName())); + PropertyValueTransformer transformer = exampleAccessor.getValueTransformerForPath(currentPath); + Optional optionalValue = transformer + .apply(Optional.ofNullable(beanWrapper.getPropertyValue(attribute.getName()))); - if (attributeValue == null) { + if (!optionalValue.isPresent()) { if (exampleAccessor.getNullHandler().equals(ExampleMatcher.NullHandler.INCLUDE)) { predicates.add(cb.isNull(from.get(attribute))); @@ -122,6 +126,8 @@ public class QueryByExamplePredicateBuilder { continue; } + Object attributeValue = optionalValue.get(); + if (attribute.getPersistentAttributeType().equals(PersistentAttributeType.EMBEDDED)) { predicates.addAll(getPredicates(currentPath, cb, from.get(attribute.getName()), @@ -132,8 +138,8 @@ public class QueryByExamplePredicateBuilder { if (isAssociation(attribute)) { if (!(from instanceof From)) { - throw new JpaSystemException(new IllegalArgumentException( - String.format("Unexpected path type for %s. Found %s where From.class was expected.", currentPath, from))); + throw new JpaSystemException(new IllegalArgumentException(String + .format("Unexpected path type for %s. Found %s where From.class was expected.", currentPath, from))); } PathNode node = currentNode.add(attribute.getName(), attributeValue); @@ -197,11 +203,11 @@ public class QueryByExamplePredicateBuilder { private static class PathNode { String name; - PathNode parent; - List siblings = new ArrayList();; - Object value; + @Nullable PathNode parent; + List siblings = new ArrayList<>(); + @Nullable Object value; - public PathNode(String edge, PathNode parent, Object value) { + PathNode(String edge, @Nullable PathNode parent, @Nullable Object value) { this.name = edge; this.parent = parent; @@ -222,14 +228,14 @@ public class QueryByExamplePredicateBuilder { } String identityHex = ObjectUtils.getIdentityHexString(value); - PathNode tmp = parent; + PathNode current = parent; - while (tmp != null) { + while (current != null) { - if (ObjectUtils.getIdentityHexString(tmp.value).equals(identityHex)) { + if (ObjectUtils.getIdentityHexString(current.value).equals(identityHex)) { return true; } - tmp = tmp.parent; + current = current.parent; } return false; diff --git a/src/main/java/org/springframework/data/jpa/convert/package-info.java b/src/main/java/org/springframework/data/jpa/convert/package-info.java new file mode 100644 index 000000000..a19e27cbb --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/convert/package-info.java @@ -0,0 +1,7 @@ +/** + * Spring Data JPA specific converter infrastructure. + */ +@NonNullApi +package org.springframework.data.jpa.convert; + +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java b/src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java new file mode 100644 index 000000000..4c7354fa5 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/convert/threeten/package-info.java @@ -0,0 +1,7 @@ +/** + * Spring Data JPA specific JSR-310 converters. + */ +@NonNullApi +package org.springframework.data.jpa.convert.threeten; + +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java b/src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java new file mode 100644 index 000000000..94a0241e5 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/convert/threetenbp/package-info.java @@ -0,0 +1,7 @@ +/** + * Spring Data JPA specific ThreeTenBp converters. + */ +@NonNullApi +package org.springframework.data.jpa.convert.threetenbp; + +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java b/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java index 328982f2d..259e6b700 100644 --- a/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java +++ b/src/main/java/org/springframework/data/jpa/domain/AbstractAuditable.java @@ -27,12 +27,14 @@ import javax.persistence.Temporal; import javax.persistence.TemporalType; import org.springframework.data.domain.Auditable; +import org.springframework.lang.Nullable; /** * Abstract base class for auditable entities. Stores the audition values in persistent fields. * * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch * @param the auditing type. Typically some kind of user. * @param the type of the auditing type's idenifier */ @@ -43,16 +45,16 @@ public abstract class AbstractAuditable extends Abst private static final long serialVersionUID = 141481953116476081L; @ManyToOne // - private U createdBy; + private @Nullable U createdBy; @Temporal(TemporalType.TIMESTAMP) // - private Date createdDate; + private @Nullable Date createdDate; @ManyToOne // - private U lastModifiedBy; + private @Nullable U lastModifiedBy; @Temporal(TemporalType.TIMESTAMP) // - private Date lastModifiedDate; + private @Nullable Date lastModifiedDate; /* * (non-Javadoc) diff --git a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java b/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java index 706570c7e..87de1a9db 100644 --- a/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java +++ b/src/main/java/org/springframework/data/jpa/domain/AbstractPersistable.java @@ -23,6 +23,7 @@ import javax.persistence.MappedSuperclass; import javax.persistence.Transient; import org.springframework.data.domain.Persistable; +import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; /** @@ -31,6 +32,7 @@ import org.springframework.util.ClassUtils; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch * @param the type of the identifier. */ @MappedSuperclass @@ -38,13 +40,13 @@ public abstract class AbstractPersistable implements Pe private static final long serialVersionUID = -5554308939380869754L; - @Id @GeneratedValue private PK id; + @Id @GeneratedValue private @Nullable PK id; /* * (non-Javadoc) * @see org.springframework.data.domain.Persistable#getId() */ - public PK getId() { + public @Nullable PK getId() { return id; } @@ -53,7 +55,7 @@ public abstract class AbstractPersistable implements Pe * * @param id the id to set */ - protected void setId(final PK id) { + protected void setId(@Nullable PK id) { this.id = id; } diff --git a/src/main/java/org/springframework/data/jpa/domain/JpaSort.java b/src/main/java/org/springframework/data/jpa/domain/JpaSort.java index 7d2045777..ef784725f 100644 --- a/src/main/java/org/springframework/data/jpa/domain/JpaSort.java +++ b/src/main/java/org/springframework/data/jpa/domain/JpaSort.java @@ -1,5 +1,5 @@ /* - * Copyright 2013-2016 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.PluralAttribute; import org.springframework.data.domain.Sort; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -81,7 +82,7 @@ public class JpaSort extends Sort { } @SuppressWarnings("deprecation") - private JpaSort(List orders, Direction direction, List> paths) { + private JpaSort(List orders, @Nullable Direction direction, List> paths) { super(combine(orders, direction, paths)); } @@ -97,7 +98,7 @@ public class JpaSort extends Sort { * @param attributes must not be {@literal null}. * @return */ - public JpaSort and(Direction direction, Attribute... attributes) { + public JpaSort and(@Nullable Direction direction, Attribute... attributes) { Assert.notNull(attributes, "Attributes must not be null!"); @@ -111,7 +112,7 @@ public class JpaSort extends Sort { * @param paths must not be {@literal null}. * @return */ - public JpaSort and(Direction direction, Path... paths) { + public JpaSort and(@Nullable Direction direction, Path... paths) { Assert.notNull(paths, "Paths must not be null!"); @@ -131,7 +132,7 @@ public class JpaSort extends Sort { * @param properties must not be {@literal null} or empty. * @return */ - public JpaSort andUnsafe(Direction direction, String... properties) { + public JpaSort andUnsafe(@Nullable Direction direction, String... properties) { Assert.notEmpty(properties, "Properties must not be null!"); @@ -168,7 +169,7 @@ public class JpaSort extends Sort { return paths; } - private static List combine(List orders, Direction direction, List> paths) { + private static List combine(List orders, @Nullable Direction direction, List> paths) { List result = new ArrayList(orders); @@ -331,7 +332,7 @@ public class JpaSort extends Sort { * @param direction can be {@literal null}, will default to {@link Sort#DEFAULT_DIRECTION}. * @param property must not be {@literal null}. */ - private JpaOrder(Direction direction, String property) { + private JpaOrder(@Nullable Direction direction, String property) { this(direction, property, NullHandling.NATIVE); } @@ -343,11 +344,11 @@ public class JpaSort extends Sort { * @param property must not be {@literal null}. * @param nullHandlingHint can be {@literal null}, will default to {@link NullHandling#NATIVE}. */ - private JpaOrder(Direction direction, String property, NullHandling nullHandlingHint) { + private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandlingHint) { this(direction, property, nullHandlingHint, false, true); } - private JpaOrder(Direction direction, String property, NullHandling nullHandling, boolean ignoreCase, + private JpaOrder(@Nullable Direction direction, String property, NullHandling nullHandling, boolean ignoreCase, boolean unsafe) { super(direction, property, nullHandling); diff --git a/src/main/java/org/springframework/data/jpa/domain/Specification.java b/src/main/java/org/springframework/data/jpa/domain/Specification.java index d30728fc7..696f791c7 100644 --- a/src/main/java/org/springframework/data/jpa/domain/Specification.java +++ b/src/main/java/org/springframework/data/jpa/domain/Specification.java @@ -24,6 +24,8 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.springframework.lang.Nullable; + /** * Specification in the sense of Domain Driven Design. * @@ -31,6 +33,7 @@ import javax.persistence.criteria.Root; * @author Thomas Darimont * @author Krzysztof Rzymkowski * @author Sebastian Staudt + * @author Mark Paluch */ @SuppressWarnings("deprecation") public interface Specification extends Serializable { @@ -91,5 +94,6 @@ public interface Specification extends Serializable { * @param query * @return a {@link Predicate}, may be {@literal null}. */ + @Nullable Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb); } diff --git a/src/main/java/org/springframework/data/jpa/domain/Specifications.java b/src/main/java/org/springframework/data/jpa/domain/Specifications.java index 20dd8b21c..affd73589 100644 --- a/src/main/java/org/springframework/data/jpa/domain/Specifications.java +++ b/src/main/java/org/springframework/data/jpa/domain/Specifications.java @@ -24,12 +24,16 @@ import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; +import org.springframework.lang.Nullable; +import org.springframework.util.Assert; + /** * Helper class to easily combine {@link Specification} instances. * * @author Oliver Gierke * @author Thomas Darimont * @author Sebastian Staudt + * @author Mark Paluch * @deprecated since 2.0, use factory methods on {@link Specification} instead. */ @Deprecated @@ -37,14 +41,14 @@ public class Specifications implements Specification, Serializable { private static final long serialVersionUID = 1L; - private final Specification spec; + private final @Nullable Specification spec; /** * Creates a new {@link Specifications} wrapper for the given {@link Specification}. * * @param spec can be {@literal null}. */ - Specifications(Specification spec) { + Specifications(@Nullable Specification spec) { this.spec = spec; } @@ -57,7 +61,7 @@ public class Specifications implements Specification, Serializable { * @return */ @Deprecated - public static Specifications where(Specification spec) { + public static Specifications where(@Nullable Specification spec) { return new Specifications<>(spec); } @@ -70,7 +74,7 @@ public class Specifications implements Specification, Serializable { * @return */ @Deprecated - public Specifications and(Specification other) { + public Specifications and(@Nullable Specification other) { return new Specifications<>(composed(spec, other, AND)); } @@ -83,7 +87,7 @@ public class Specifications implements Specification, Serializable { * @return */ @Deprecated - public Specifications or(Specification other) { + public Specifications or(@Nullable Specification other) { return new Specifications<>(composed(spec, other, OR)); } @@ -96,7 +100,7 @@ public class Specifications implements Specification, Serializable { * @return */ @Deprecated - public static Specifications not(Specification spec) { + public static Specifications not(@Nullable Specification spec) { return new Specifications<>(negated(spec)); } @@ -104,6 +108,7 @@ public class Specifications implements Specification, Serializable { * (non-Javadoc) * @see org.springframework.data.jpa.domain.Specification#toPredicate(javax.persistence.criteria.Root, javax.persistence.criteria.CriteriaQuery, javax.persistence.criteria.CriteriaBuilder) */ + @Nullable public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder builder) { return spec == null ? null : spec.toPredicate(root, query, builder); } @@ -133,11 +138,11 @@ public class Specifications implements Specification, Serializable { abstract Predicate combine(CriteriaBuilder builder, Predicate lhs, Predicate rhs); } - static Specification negated(Specification spec) { + static Specification negated(@Nullable Specification spec) { return (root, query, builder) -> spec == null ? null : builder.not(spec.toPredicate(root, query, builder)); } - static Specification composed(Specification lhs, Specification rhs, CompositionType compositionType) { + static Specification composed(@Nullable Specification lhs, @Nullable Specification rhs, CompositionType compositionType) { return (root, query, builder) -> { diff --git a/src/main/java/org/springframework/data/jpa/domain/package-info.java b/src/main/java/org/springframework/data/jpa/domain/package-info.java index 996a45bc8..67a99500a 100644 --- a/src/main/java/org/springframework/data/jpa/domain/package-info.java +++ b/src/main/java/org/springframework/data/jpa/domain/package-info.java @@ -1,5 +1,7 @@ /** * JPA specific support classes to implement domain classes. */ +@NonNullApi package org.springframework.data.jpa.domain; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java index d53d2dbc5..f07daab0b 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/AuditingEntityListener.java @@ -22,6 +22,7 @@ import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Configurable; import org.springframework.data.auditing.AuditingHandler; import org.springframework.data.domain.Auditable; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -55,11 +56,12 @@ import org.springframework.util.Assert; * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ @Configurable public class AuditingEntityListener { - private ObjectFactory handler; + private @Nullable ObjectFactory handler; /** * Configures the {@link AuditingHandler} to be used to set the current auditor on the domain types touched. @@ -84,7 +86,11 @@ public class AuditingEntityListener { Assert.notNull(target, "Entity must not be null!"); if (handler != null) { - handler.getObject().markCreated(target); + + AuditingHandler object = handler.getObject(); + if (object != null) { + object.markCreated(target); + } } } @@ -100,7 +106,11 @@ public class AuditingEntityListener { Assert.notNull(target, "Entity must not be null!"); if (handler != null) { - handler.getObject().markModified(target); + + AuditingHandler object = handler.getObject(); + if (object != null) { + object.markModified(target); + } } } } diff --git a/src/main/java/org/springframework/data/jpa/domain/support/package-info.java b/src/main/java/org/springframework/data/jpa/domain/support/package-info.java index 4135d713c..7bbd85d1a 100644 --- a/src/main/java/org/springframework/data/jpa/domain/support/package-info.java +++ b/src/main/java/org/springframework/data/jpa/domain/support/package-info.java @@ -1,5 +1,7 @@ /** * Implementation classes for auditing with JPA. */ +@NonNullApi package org.springframework.data.jpa.domain.support; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java index ec6dde0ee..af40de93e 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaMetamodelMappingContext.java @@ -26,6 +26,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -33,6 +34,7 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch * @since 1.3 */ public class JpaMetamodelMappingContext @@ -73,6 +75,11 @@ public class JpaMetamodelMappingContext SimpleTypeHolder simpleTypeHolder) { Metamodel metamodel = getMetamodelFor(owner.getType()); + + if (metamodel == null) { + throw new IllegalStateException(String.format("Metamodel for %s not available!", owner.getType())); + } + return new JpaPersistentPropertyImpl(metamodel, property, owner, simpleTypeHolder); } @@ -91,6 +98,7 @@ public class JpaMetamodelMappingContext * @param type * @return */ + @Nullable private Metamodel getMetamodelFor(Class type) { for (Metamodel model : models) { diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java index daf93c4cc..1fe23c28f 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentEntityImpl.java @@ -31,6 +31,7 @@ import org.springframework.util.Assert; * @author Oliver Gierke * @author Greg Turnquist * @author Christoph Strobl + * @author Mark Paluch * @since 1.3 */ class JpaPersistentEntityImpl extends BasicPersistentEntity @@ -112,7 +113,7 @@ class JpaPersistentEntityImpl extends BasicPersistentEntity entity, Object bean, + JpaProxyAwareIdentifierAccessor(JpaPersistentEntity entity, Object bean, ProxyIdAccessor proxyIdAccessor) { super(entity, bean); diff --git a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java index 2fa61af99..51000b98a 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java +++ b/src/main/java/org/springframework/data/jpa/mapping/JpaPersistentPropertyImpl.java @@ -47,6 +47,7 @@ import org.springframework.data.mapping.model.Property; import org.springframework.data.mapping.model.SimpleTypeHolder; import org.springframework.data.util.ClassTypeInformation; import org.springframework.data.util.TypeInformation; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -55,6 +56,7 @@ import org.springframework.util.Assert; * @author Oliver Gierke * @author Greg Turnquist * @author Christoph Strobl + * @author Mark Paluch * @since 1.3 */ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty @@ -88,8 +90,8 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty associationTargetType; + private final @Nullable Boolean usePropertyAccess; + private final @Nullable TypeInformation associationTargetType; private final boolean updateable; private final JpaMetamodel metamodel; @@ -220,6 +222,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty detectAssociationTargetType() { if (!isAssociation()) { @@ -287,7 +291,7 @@ class JpaPersistentPropertyImpl extends AnnotationBasedPersistentProperty annotationType : UPDATEABLE_ANNOTATIONS) { diff --git a/src/main/java/org/springframework/data/jpa/mapping/package-info.java b/src/main/java/org/springframework/data/jpa/mapping/package-info.java index 9df14027a..918ef4b44 100644 --- a/src/main/java/org/springframework/data/jpa/mapping/package-info.java +++ b/src/main/java/org/springframework/data/jpa/mapping/package-info.java @@ -1,5 +1,7 @@ /** * JPA specific support classes for the Spring Data mapping subsystem. */ +@NonNullApi package org.springframework.data.jpa.mapping; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java b/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java index a5062f699..7899a4743 100644 --- a/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java +++ b/src/main/java/org/springframework/data/jpa/provider/HibernateUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2016 the original author or authors. + * Copyright 2016-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.util.Arrays; import java.util.List; import org.hibernate.Query; +import org.springframework.lang.Nullable; import org.springframework.util.ClassUtils; import org.springframework.util.ReflectionUtils; @@ -27,6 +28,8 @@ import org.springframework.util.ReflectionUtils; * Utility functions to work with Hibernate. Mostly using reflection to make sure common functionality can be executed * against all the Hibernate version we support. * + * @author Oliver Gierke + * @author Mark Paluch * @since 1.10.2 * @soundtrack Benny Greb - Soulfood (Live, https://www.youtube.com/watch?v=9_ErMa_CtSw) */ @@ -34,10 +37,9 @@ public abstract class HibernateUtils { private static final List TYPES = Arrays.asList("org.hibernate.jpa.HibernateQuery", "org.hibernate.ejb.HibernateQuery"); - private static final Method GET_HIBERNATE_QUERY; - - private static final Class HIBERNATE_QUERY_INTERFACE; - private static final Method QUERY_STRING_METHOD; + private static final @Nullable Method GET_HIBERNATE_QUERY; + private static final @Nullable Class HIBERNATE_QUERY_INTERFACE; + private static final @Nullable Method QUERY_STRING_METHOD; private HibernateUtils() {} @@ -73,6 +75,7 @@ public abstract class HibernateUtils { * @param query * @return */ + @Nullable public static String getHibernateQuery(Object query) { if (HIBERNATE_QUERY_INTERFACE != null && QUERY_STRING_METHOD != null @@ -84,6 +87,12 @@ public abstract class HibernateUtils { query = ((javax.persistence.Query) query).unwrap(HIBERNATE_QUERY_INTERFACE); } - return ((Query) ReflectionUtils.invokeMethod(GET_HIBERNATE_QUERY, query)).getQueryString(); + if (GET_HIBERNATE_QUERY == null) { + throw new IllegalStateException( + "Cannot invoke getHibernateQuery(…). No underlying method for a reflective call found."); + } + + Query q = (Query) ReflectionUtils.invokeMethod(GET_HIBERNATE_QUERY, query); + return q == null ? "" : q.getQueryString(); } } diff --git a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java index bbfafc30b..f86ce6949 100644 --- a/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java +++ b/src/main/java/org/springframework/data/jpa/provider/PersistenceProvider.java @@ -21,6 +21,7 @@ import static org.springframework.data.jpa.provider.PersistenceProvider.Constant import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.NoSuchElementException; import javax.persistence.EntityManager; import javax.persistence.Query; @@ -32,6 +33,7 @@ import org.hibernate.ScrollMode; import org.hibernate.ScrollableResults; import org.hibernate.proxy.HibernateProxy; import org.springframework.data.util.CloseableIterator; +import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; import org.springframework.util.ConcurrentReferenceHashMap; @@ -41,6 +43,7 @@ import org.springframework.util.ConcurrentReferenceHashMap; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { @@ -94,8 +97,9 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { * (non-Javadoc) * @see org.springframework.data.jpa.provider.PersistenceProvider#potentiallyConvertEmptyCollection(java.util.Collection) */ + @Nullable @Override - public Collection potentiallyConvertEmptyCollection(Collection collection) { + public Collection potentiallyConvertEmptyCollection(@Nullable Collection collection) { return collection == null || collection.isEmpty() ? null : collection; } @@ -132,6 +136,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { * (non-Javadoc) * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) */ + @Nullable @Override public Object getIdentifierFrom(Object entity) { return null; @@ -140,8 +145,9 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { /* (non-Javadoc) * @see org.springframework.data.jpa.provider.PersistenceProvider#potentiallyConvertEmptyCollection(java.util.Collection) */ + @Nullable @Override - public Collection potentiallyConvertEmptyCollection(Collection collection) { + public Collection potentiallyConvertEmptyCollection(@Nullable Collection collection) { return collection == null || collection.isEmpty() ? null : collection; } @@ -164,6 +170,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { * (non-Javadoc) * @see org.springframework.data.jpa.repository.query.QueryExtractor#extractQueryString(javax.persistence.Query) */ + @Nullable @Override public String extractQueryString(Query query) { return null; @@ -191,6 +198,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { * (non-Javadoc) * @see org.springframework.data.jpa.repository.support.ProxyIdAccessor#getIdentifierFrom(java.lang.Object) */ + @Nullable @Override public Object getIdentifierFrom(Object entity) { return null; @@ -331,7 +339,8 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { * @param collection * @return */ - public Collection potentiallyConvertEmptyCollection(Collection collection) { + @Nullable + public Collection potentiallyConvertEmptyCollection(@Nullable Collection collection) { return collection; } @@ -350,7 +359,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { */ private static class HibernateScrollableResultsIterator implements CloseableIterator { - private final ScrollableResults scrollableResults; + private final @Nullable ScrollableResults scrollableResults; /** * Creates a new {@link HibernateScrollableResultsIterator} for the given {@link Query}. @@ -371,6 +380,10 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { @Override public Object next() { + if (scrollableResults == null) { + throw new NoSuchElementException("No ScrollableResults"); + } + Object[] row = scrollableResults.get(); return row.length == 1 ? row[0] : row; @@ -382,7 +395,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { */ @Override public boolean hasNext() { - return scrollableResults == null ? false : scrollableResults.next(); + return scrollableResults != null && scrollableResults.next(); } /* @@ -409,7 +422,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { @SuppressWarnings("unchecked") private static class EclipseLinkScrollableResultsIterator implements CloseableIterator { - private final ScrollableCursor scrollableCursor; + private final @Nullable ScrollableCursor scrollableCursor; /** * Creates a new {@link EclipseLinkScrollableResultsIterator} for the given JPA {@link Query}. @@ -429,7 +442,7 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { */ @Override public boolean hasNext() { - return scrollableCursor == null ? false : scrollableCursor.hasNext(); + return scrollableCursor != null && scrollableCursor.hasNext(); } /* @@ -438,6 +451,11 @@ public enum PersistenceProvider implements QueryExtractor, ProxyIdAccessor { */ @Override public T next() { + + if (scrollableCursor == null) { + throw new NoSuchElementException("No ScrollableCursor"); + } + return (T) scrollableCursor.next(); } diff --git a/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java b/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java index 4a319662d..98baccd0a 100644 --- a/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java +++ b/src/main/java/org/springframework/data/jpa/provider/ProxyIdAccessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,13 @@ */ package org.springframework.data.jpa.provider; +import org.springframework.lang.Nullable; + /** * Interface for a persistence provider specific accessor of identifiers held in proxies. * * @author Oliver Gierke + * @author Mark Paluch */ public interface ProxyIdAccessor { @@ -37,5 +40,6 @@ public interface ProxyIdAccessor { * @param entity must not be {@literal null}. * @return */ + @Nullable Object getIdentifierFrom(Object entity); } diff --git a/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java b/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java index 244b5531a..c8fe9080c 100644 --- a/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java +++ b/src/main/java/org/springframework/data/jpa/provider/QueryExtractor.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2011 the original author or authors. + * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,10 +17,13 @@ package org.springframework.data.jpa.provider; import javax.persistence.Query; +import org.springframework.lang.Nullable; + /** * Interface to hide different implementations to extract the original JPA query string from a {@link Query}. * * @author Oliver Gierke + * @author Mark Paluch */ public interface QueryExtractor { @@ -31,6 +34,7 @@ public interface QueryExtractor { * @param query * @return the query string representing the query or {@literal null} if resolving is not possible. */ + @Nullable String extractQueryString(Query query); /** diff --git a/src/main/java/org/springframework/data/jpa/provider/package-info.java b/src/main/java/org/springframework/data/jpa/provider/package-info.java new file mode 100644 index 000000000..39a8e1ef9 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/provider/package-info.java @@ -0,0 +1,7 @@ +/** + * JPA provider-specific utilities. + */ +@NonNullApi +package org.springframework.data.jpa.provider; + +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java b/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java index 40b001320..e647519b8 100644 --- a/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java +++ b/src/main/java/org/springframework/data/jpa/repository/JpaSpecificationExecutor.java @@ -22,6 +22,7 @@ import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.data.jpa.domain.Specification; +import org.springframework.lang.Nullable; /** * Interface to allow execution of {@link Specification}s based on the JPA criteria API. @@ -38,7 +39,7 @@ public interface JpaSpecificationExecutor { * @return never {@literal null}. * @throws org.springframework.dao.IncorrectResultSizeDataAccessException if more than one entity found. */ - Optional findOne(Specification spec); + Optional findOne(@Nullable Specification spec); /** * Returns all entities matching the given {@link Specification}. @@ -46,25 +47,25 @@ public interface JpaSpecificationExecutor { * @param spec can be {@literal null}. * @return never {@literal null}. */ - List findAll(Specification spec); + List findAll(@Nullable Specification spec); /** * Returns a {@link Page} of entities matching the given {@link Specification}. * * @param spec can be {@literal null}. - * @param pageable can be {@literal null}. + * @param pageable must not be {@literal null}. * @return never {@literal null}. */ - Page findAll(Specification spec, Pageable pageable); + Page findAll(@Nullable Specification spec, Pageable pageable); /** * Returns all entities matching the given {@link Specification} and {@link Sort}. * * @param spec can be {@literal null}. - * @param sort can be {@literal null}. + * @param sort must not be {@literal null}. * @return never {@literal null}. */ - List findAll(Specification spec, Sort sort); + List findAll(@Nullable Specification spec, Sort sort); /** * Returns the number of instances that the given {@link Specification} will return. @@ -72,5 +73,5 @@ public interface JpaSpecificationExecutor { * @param spec the {@link Specification} to count instances for. Can be {@literal null}. * @return the number of instances. */ - long count(Specification spec); + long count(@Nullable Specification spec); } diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java index e86107411..3d721c43c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryBean.java @@ -49,7 +49,7 @@ class JpaRepositoryBean extends CdiRepositoryBean { * @param entityManagerBean must not be {@literal null}. * @param qualifiers must not be {@literal null}. * @param repositoryType must not be {@literal null}. - * @param detector can be {@literal null}. + * @param detector can be {@link Optional#empty()}. */ JpaRepositoryBean(BeanManager beanManager, Bean entityManagerBean, Set qualifiers, Class repositoryType, Optional detector) { diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java index cc4656573..fd22e1636 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/JpaRepositoryExtension.java @@ -123,6 +123,6 @@ public class JpaRepositoryExtension extends CdiRepositoryExtensionSupport { // Construct and return the repository bean. return new JpaRepositoryBean(beanManager, entityManagerBean, qualifiers, repositoryType, - Optional.ofNullable(getCustomImplementationDetector())); + Optional.of(getCustomImplementationDetector())); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java b/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java index 9c7fadf17..2b6788bd0 100644 --- a/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java +++ b/src/main/java/org/springframework/data/jpa/repository/cdi/package-info.java @@ -1,5 +1,7 @@ /** * CDI support for Spring Data JPA Repositories. */ +@NonNullApi package org.springframework.data.jpa.repository.cdi; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java index a12e1f2bc..fd6c42164 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaMetamodelMappingContextFactoryBean.java @@ -1,3 +1,18 @@ +/* + * Copyright 2017 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ package org.springframework.data.jpa.repository.config; import java.util.Collection; @@ -16,17 +31,19 @@ import org.springframework.beans.factory.config.AbstractFactoryBean; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.data.jpa.mapping.JpaMetamodelMappingContext; +import org.springframework.lang.Nullable; /** * {@link FactoryBean} to setup {@link JpaMetamodelMappingContext} instances from Spring configuration. * * @author Oliver Gierke + * @author Mark Paluch * @since 1.6 */ -class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean implements - ApplicationContextAware { +class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean + implements ApplicationContextAware { - private ListableBeanFactory beanFactory; + private @Nullable ListableBeanFactory beanFactory; /* * (non-Javadoc) @@ -82,8 +99,12 @@ class JpaMetamodelMappingContextFactoryBean extends AbstractFactoryBean getMetamodels() { - Collection factories = BeanFactoryUtils.beansOfTypeIncludingAncestors(beanFactory, - EntityManagerFactory.class).values(); + if (beanFactory == null) { + throw new IllegalStateException("BeanFactory must not be null!"); + } + + Collection factories = BeanFactoryUtils + .beansOfTypeIncludingAncestors(beanFactory, EntityManagerFactory.class).values(); Set metamodels = new HashSet(factories.size()); for (EntityManagerFactory emf : factories) { diff --git a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java index 44fd91a2b..fbbe958ca 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/JpaRepositoryConfigExtension.java @@ -55,12 +55,13 @@ import org.springframework.util.StringUtils; * {@link PersistenceUnit} annotated properties and methods) as well as * {@link PersistenceExceptionTranslationPostProcessor} to enable exception translation of persistence specific * exceptions into Spring's {@link DataAccessException} hierarchy. - * + * * @author Oliver Gierke * @author Eberhard Wolff * @author Gil Markham * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensionSupport { @@ -68,7 +69,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi private static final String DEFAULT_TRANSACTION_MANAGER_BEAN_NAME = "transactionManager"; private static final String ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE = "enableDefaultTransactions"; - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getModuleName() */ @@ -86,7 +87,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi return JpaRepositoryFactoryBean.class.getName(); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config14.RepositoryConfigurationExtensionSupport#getModulePrefix() */ @@ -95,7 +96,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi return getModuleName().toLowerCase(Locale.US); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingAnnotations() */ @@ -104,7 +105,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi return Arrays.asList(Entity.class, MappedSuperclass.class); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#getIdentifyingTypes() */ @@ -113,7 +114,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi return Collections.> singleton(JpaRepository.class); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.RepositoryConfigurationSource) */ @@ -126,7 +127,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi builder.addPropertyReference("mappingContext", JPA_MAPPING_CONTEXT_BEAN_NAME); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.AnnotationRepositoryConfigurationSource) */ @@ -139,7 +140,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi attributes.getBoolean(ENABLE_DEFAULT_TRANSACTIONS_ATTRIBUTE)); } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#postProcess(org.springframework.beans.factory.support.BeanDefinitionBuilder, org.springframework.data.repository.config.XmlRepositoryConfigurationSource) */ @@ -153,7 +154,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi } } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.config.RepositoryConfigurationExtensionSupport#registerBeansForRoot(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.data.repository.config.RepositoryConfigurationSource) */ @@ -184,7 +185,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi /** * Creates an anonymous factory to extract the actual {@link javax.persistence.EntityManager} from the * {@link javax.persistence.EntityManagerFactory} bean name reference. - * + * * @param config * @param source * @return @@ -205,8 +206,7 @@ public class JpaRepositoryConfigExtension extends RepositoryConfigurationExtensi private static String getEntityManagerBeanRef(RepositoryConfigurationSource config) { - Optional entityManagerFactoryRef = config == null ? Optional.empty() - : config.getAttribute("entityManagerFactoryRef"); + Optional entityManagerFactoryRef = config.getAttribute("entityManagerFactoryRef"); return entityManagerFactoryRef.orElse("entityManagerFactory"); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/config/package-info.java b/src/main/java/org/springframework/data/jpa/repository/config/package-info.java index 9af11c562..d70b4fa53 100644 --- a/src/main/java/org/springframework/data/jpa/repository/config/package-info.java +++ b/src/main/java/org/springframework/data/jpa/repository/config/package-info.java @@ -1,5 +1,7 @@ /** * Classes for JPA namespace configuration. */ +@NonNullApi package org.springframework.data.jpa.repository.config; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/repository/package-info.java b/src/main/java/org/springframework/data/jpa/repository/package-info.java index 814dc765a..c27c800cb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/package-info.java +++ b/src/main/java/org/springframework/data/jpa/repository/package-info.java @@ -1,5 +1,7 @@ /** * Interfaces and annotations for JPA specific repositories. */ +@NonNullApi package org.springframework.data.jpa.repository; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java index 05f35588e..8bd844377 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/AbstractJpaQuery.java @@ -42,11 +42,12 @@ import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.util.Lazy; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** * Abstract base class to implement {@link RepositoryQuery}s. - * + * * @author Oliver Gierke * @author Thomas Darimont * @author Mark Paluch @@ -63,7 +64,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Creates a new {@link AbstractJpaQuery} from the given {@link JpaQueryMethod}. - * + * * @param method * @param em */ @@ -81,13 +82,14 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { * (non-Javadoc) * @see org.springframework.data.repository.query.RepositoryQuery#getQueryMethod() */ + @Override public JpaQueryMethod getQueryMethod() { return method; } /** * Returns the {@link EntityManager}. - * + * * @return will never be {@literal null}. */ protected EntityManager getEntityManager() { @@ -96,7 +98,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Returns the {@link JpaMetamodel}. - * + * * @return */ protected JpaMetamodel getMetamodel() { @@ -107,6 +109,8 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { * (non-Javadoc) * @see org.springframework.data.repository.query.RepositoryQuery#execute(java.lang.Object[]) */ + @Nullable + @Override public Object execute(Object[] parameters) { return doExecute(getExecution(), parameters); } @@ -116,6 +120,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { * @param values * @return */ + @Nullable private Object doExecute(JpaQueryExecution execution, Object[] values) { Object result = execution.execute(this, values); @@ -147,7 +152,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Applies the declared query hints to the given query. - * + * * @param query * @return */ @@ -162,7 +167,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Protected to be able to customize in sub-classes. - * + * * @param query must not be {@literal null}. * @param hint must not be {@literal null}. */ @@ -176,7 +181,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Applies the {@link LockModeType} provided by the {@link JpaQueryMethod} to the given {@link Query}. - * + * * @param query must not be {@literal null}. * @param method must not be {@literal null}. * @return @@ -198,7 +203,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Configures the {@link javax.persistence.EntityGraph} to use for the given {@link JpaQueryMethod} if the * {@link EntityGraph} annotation is present. - * + * * @param query must not be {@literal null}. * @param method must not be {@literal null}. * @return @@ -225,7 +230,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Creates a {@link Query} instance for the given values. - * + * * @param values must not be {@literal null}. * @return */ @@ -233,7 +238,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Creates a {@link TypedQuery} for counting using the given values. - * + * * @param values must not be {@literal null}. * @return */ @@ -245,7 +250,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { /** * Creates a new {@link TupleConverter} for the given {@link ReturnedType}. - * + * * @param type must not be {@literal null}. */ public TupleConverter(ReturnedType type) { @@ -255,7 +260,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { this.type = type; } - /* + /* * (non-Javadoc) * @see org.springframework.core.convert.converter.Converter#convert(java.lang.Object) */ @@ -267,7 +272,7 @@ public abstract class AbstractJpaQuery implements RepositoryQuery { } Tuple tuple = (Tuple) source; - Map result = new HashMap(); + Map result = new HashMap<>(); List> elements = tuple.getElements(); if (elements.size() == 1) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java b/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java index 75685eaa8..388c7f047 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/DefaultJpaEntityMetadata.java @@ -23,7 +23,7 @@ import org.springframework.util.StringUtils; /** * Default implementation for {@link JpaEntityMetadata}. - * + * * @author Oliver Gierke * @author Christoph Strobl */ @@ -33,7 +33,7 @@ public class DefaultJpaEntityMetadata implements JpaEntityMetadata { /** * Creates a new {@link DefaultJpaEntityMetadata} for the given domain type. - * + * * @param domainType must not be {@literal null}. */ public DefaultJpaEntityMetadata(Class domainType) { @@ -42,7 +42,7 @@ public class DefaultJpaEntityMetadata implements JpaEntityMetadata { this.domainType = domainType; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.core.EntityMetadata#getJavaType() */ @@ -55,11 +55,10 @@ public class DefaultJpaEntityMetadata implements JpaEntityMetadata { * (non-Javadoc) * @see org.springframework.data.jpa.repository.support.JpaEntityMetadata#getEntityName() */ + @Override public String getEntityName() { Entity entity = AnnotatedElementUtils.findMergedAnnotation(domainType, Entity.class); - boolean hasName = null != entity && StringUtils.hasText(entity.name()); - - return hasName ? entity.name() : domainType.getSimpleName(); + return null != entity && StringUtils.hasText(entity.name()) ? entity.name() : domainType.getSimpleName(); } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java index c794a0f1d..1550ed1eb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/Jpa21Utils.java @@ -27,6 +27,7 @@ import javax.persistence.EntityManager; import javax.persistence.Query; import javax.persistence.Subgraph; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -39,11 +40,12 @@ import org.springframework.util.StringUtils; * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch * @since 1.6 */ public class Jpa21Utils { - private static final Method GET_ENTITY_GRAPH_METHOD; + private static final @Nullable Method GET_ENTITY_GRAPH_METHOD; private static final boolean JPA21_AVAILABLE = ClassUtils.isPresent("javax.persistence.NamedEntityGraph", Jpa21Utils.class.getClassLoader()); @@ -69,7 +71,7 @@ public class Jpa21Utils { * @return a {@code Map} with the hints or an empty {@code Map} if no hints were found. * @since 1.8 */ - public static Map tryGetFetchGraphHints(EntityManager em, JpaEntityGraph entityGraph, + public static Map tryGetFetchGraphHints(EntityManager em, @Nullable JpaEntityGraph entityGraph, Class entityType) { if (entityGraph == null) { @@ -95,6 +97,7 @@ public class Jpa21Utils { * @param entityType must not be {@literal null}. * @return the {@link EntityGraph} described by the given {@code entityGraph}. */ + @Nullable private static EntityGraph tryGetFetchGraph(EntityManager em, JpaEntityGraph jpaEntityGraph, Class entityType) { Assert.notNull(em, "EntityManager must not be null!"); @@ -157,7 +160,8 @@ public class Jpa21Utils { } } - private static void createGraph(String[] pathComponents, int offset, EntityGraph root, Subgraph parent) { + private static void createGraph(String[] pathComponents, int offset, EntityGraph root, + @Nullable Subgraph parent) { String attributeName = pathComponents[offset]; @@ -216,8 +220,9 @@ public class Jpa21Utils { * @param parent * @return {@literal null} if not found. */ + @Nullable private static AttributeNode findAttributeNode(String attributeNodeName, EntityGraph entityGraph, - Subgraph parent) { + @Nullable Subgraph parent) { return findAttributeNode(attributeNodeName, parent != null ? parent.getAttributeNodes() : entityGraph.getAttributeNodes()); } @@ -230,6 +235,7 @@ public class Jpa21Utils { * @param nodes * @return {@literal null} if not found. */ + @Nullable private static AttributeNode findAttributeNode(String attributeNodeName, List> nodes) { for (AttributeNode node : nodes) { @@ -249,6 +255,7 @@ public class Jpa21Utils { * @param node * @return */ + @Nullable private static Subgraph getSubgraph(AttributeNode node) { return node.getSubgraphs().isEmpty() ? null : node.getSubgraphs().values().iterator().next(); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java index bbbddb90a..6abf48376 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaCountQueryCreator.java @@ -24,12 +24,14 @@ import javax.persistence.criteria.Root; import org.springframework.data.domain.Sort; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.Nullable; /** * Special {@link JpaQueryCreator} that creates a count projecting query. * * @author Oliver Gierke * @author Marc Lefrançois + * @author Mark Paluch */ public class JpaCountQueryCreator extends JpaQueryCreator { @@ -61,7 +63,7 @@ public class JpaCountQueryCreator extends JpaQueryCreator { */ @Override @SuppressWarnings("unchecked") - protected CriteriaQuery complete(Predicate predicate, Sort sort, + protected CriteriaQuery complete(@Nullable Predicate predicate, Sort sort, CriteriaQuery query, CriteriaBuilder builder, Root root) { CriteriaQuery select = query.select(getCountQuery(query, builder, root)); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java index f442330da..b4968645d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaEntityGraph.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,6 +20,7 @@ import java.util.List; import org.springframework.data.jpa.repository.EntityGraph; import org.springframework.data.jpa.repository.EntityGraph.EntityGraphType; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -27,6 +28,7 @@ import org.springframework.util.StringUtils; * EntityGraph configuration for JPA 2.1 {@link EntityGraph}s. * * @author Thomas Darimont + * @author Mark Paluch * @since 1.6 */ public class JpaEntityGraph { @@ -56,7 +58,7 @@ public class JpaEntityGraph { * @param attributePaths may be {@literal null}. * @since 1.9 */ - public JpaEntityGraph(String name, EntityGraphType type, String[] attributePaths) { + public JpaEntityGraph(String name, EntityGraphType type, @Nullable String[] attributePaths) { Assert.hasText(name, "The name of an EntityGraph must not be null or empty!"); Assert.notNull(type, "FetchGraphType must not be null!"); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java index fc3915bf6..d7a8e71cb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaParameters.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -26,11 +26,13 @@ import org.springframework.data.jpa.repository.Temporal; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; +import org.springframework.lang.Nullable; /** * Custom extension of {@link Parameters} discovering additional query parameter annotations. * * @author Thomas Darimont + * @author Mark Paluch */ public class JpaParameters extends Parameters { @@ -73,8 +75,8 @@ public class JpaParameters extends Parameters { */ static class JpaParameter extends Parameter { - private final Temporal annotation; - private TemporalType temporalType; + private final @Nullable Temporal annotation; + private @Nullable TemporalType temporalType; /** * Creates a new {@link JpaParameter}. @@ -89,8 +91,8 @@ public class JpaParameters extends Parameters { this.temporalType = null; if (!isDateParameter() && hasTemporalParamAnnotation()) { - throw new IllegalArgumentException(Temporal.class.getSimpleName() - + " annotation is only allowed on Date parameter!"); + throw new IllegalArgumentException( + Temporal.class.getSimpleName() + " annotation is only allowed on Date parameter!"); } } @@ -106,14 +108,15 @@ public class JpaParameters extends Parameters { /** * @return {@literal true} if this parameter is of type {@link Date} and has an {@link Temporal} annotation. */ - public boolean isTemporalParameter() { + boolean isTemporalParameter() { return isDateParameter() && hasTemporalParamAnnotation(); } /** * @return the {@link TemporalType} on the {@link Temporal} annotation of the given {@link Parameter}. */ - public TemporalType getTemporalType() { + @Nullable + TemporalType getTemporalType() { if (temporalType == null) { this.temporalType = annotation == null ? null : annotation.value(); @@ -122,6 +125,22 @@ public class JpaParameters extends Parameters { return this.temporalType; } + /** + * @return the required {@link TemporalType} on the {@link Temporal} annotation of the given {@link Parameter}. + * @throws IllegalStateException if the parameter does not define a {@link TemporalType}. + * @since 2.0 + */ + TemporalType getRequiredTemporalType() throws IllegalStateException { + + TemporalType temporalType = getTemporalType(); + + if (temporalType != null) { + return temporalType; + } + + throw new IllegalStateException(String.format("Required temporal type not found for %s!", getType())); + } + private boolean hasTemporalParamAnnotation() { return annotation != null; } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java index b1da74c6d..40bb4184b 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryCreator.java @@ -42,14 +42,16 @@ import org.springframework.data.repository.query.parser.AbstractQueryCreator; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** * Query creator to create a {@link CriteriaQuery} from a {@link PartTree}. - * + * * @author Oliver Gierke * @author Mark Paluch * @author Michael Cramer + * @author Mark Paluch */ public class JpaQueryCreator extends AbstractQueryCreator, Predicate> { @@ -62,7 +64,7 @@ public class JpaQueryCreator extends AbstractQueryCreator> getParameterExpressions() { @@ -148,7 +150,7 @@ public class JpaQueryCreator extends AbstractQueryCreator complete(Predicate predicate, Sort sort, + protected CriteriaQuery complete(@Nullable Predicate predicate, Sort sort, CriteriaQuery query, CriteriaBuilder builder, Root root) { if (returnedType.needsCustomConstruction()) { - List> selections = new ArrayList>(); + List> selections = new ArrayList<>(); for (String property : returnedType.getInputProperties()) { @@ -207,7 +209,7 @@ public class JpaQueryCreator extends AbstractQueryCreator returnType = method.getReturnType(); @@ -363,10 +366,7 @@ public abstract class JpaQueryExecution { Class optionalType = ClassUtils.forName("java.util.Optional", classLoader); conversionService.removeConvertible(Object.class, optionalType); - } catch (ClassNotFoundException e) { - return; - } catch (LinkageError e) { - return; + } catch (ClassNotFoundException | LinkageError o_O) { } } } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java index 20d2a94ed..9e9b9c8d7 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryFactory.java @@ -1,5 +1,5 @@ /* - * Copyright 2013 the original author or authors. + * Copyright 2013-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,11 +24,13 @@ import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryMethod; import org.springframework.data.repository.query.RepositoryQuery; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; /** * Factory to create the appropriate {@link RepositoryQuery} for a {@link JpaQueryMethod}. * * @author Thomas Darimont + * @author Mark Paluch */ enum JpaQueryFactory { @@ -46,6 +48,7 @@ enum JpaQueryFactory { * @param evaluationContextProvider * @return the {@link RepositoryQuery} derived from the annotation or {@code null} if no annotation found. */ + @Nullable AbstractJpaQuery fromQueryAnnotation(JpaQueryMethod method, EntityManager em, EvaluationContextProvider evaluationContextProvider) { @@ -62,7 +65,8 @@ enum JpaQueryFactory { * @param evaluationContextProvider * @return */ - AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, String queryString, + @Nullable + AbstractJpaQuery fromMethodWithQueryString(JpaQueryMethod method, EntityManager em, @Nullable String queryString, EvaluationContextProvider evaluationContextProvider) { if (queryString == null) { @@ -80,6 +84,7 @@ enum JpaQueryFactory { * @param em must not be {@literal null}. * @return */ + @Nullable public StoredProcedureJpaQuery fromProcedureAnnotation(JpaQueryMethod method, EntityManager em) { if (!method.isProcedureQuery()) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java index cbafb058d..9cee79e81 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryLookupStrategy.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2016 the original author or authors. + * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -28,13 +28,15 @@ import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** * Query lookup strategy to execute finders. - * + * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ public final class JpaQueryLookupStrategy { @@ -45,7 +47,7 @@ public final class JpaQueryLookupStrategy { /** * Base class for {@link QueryLookupStrategy} implementations that need access to an {@link EntityManager}. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -56,7 +58,7 @@ public final class JpaQueryLookupStrategy { /** * Creates a new {@link AbstractQueryLookupStrategy}. - * + * * @param em * @param extractor * @param evaluationContextProvider @@ -67,7 +69,7 @@ public final class JpaQueryLookupStrategy { this.provider = extractor; } - /* + /* * (non-Javadoc) * @see org.springframework.data.repository.query.QueryLookupStrategy#resolveQuery(java.lang.reflect.Method, org.springframework.data.repository.core.RepositoryMetadata, org.springframework.data.projection.ProjectionFactory, org.springframework.data.repository.core.NamedQueries) */ @@ -82,7 +84,7 @@ public final class JpaQueryLookupStrategy { /** * {@link QueryLookupStrategy} to create a query from the method name. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -112,7 +114,7 @@ public final class JpaQueryLookupStrategy { /** * {@link QueryLookupStrategy} that tries to detect a declared query declared via {@link Query} annotation followed by * a JPA named query lookup. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -122,7 +124,7 @@ public final class JpaQueryLookupStrategy { /** * Creates a new {@link DeclaredQueryLookupStrategy}. - * + * * @param em * @param extractor * @param evaluationContextProvider @@ -174,7 +176,7 @@ public final class JpaQueryLookupStrategy { * {@link QueryLookupStrategy} to try to detect a declared query first ( * {@link org.springframework.data.jpa.repository.Query}, JPA named query). In case none is found we fall back on * query creation. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -185,7 +187,7 @@ public final class JpaQueryLookupStrategy { /** * Creates a new {@link CreateIfNotFoundQueryLookupStrategy}. - * + * * @param em * @param extractor * @param createStrategy @@ -218,14 +220,14 @@ public final class JpaQueryLookupStrategy { /** * Creates a {@link QueryLookupStrategy} for the given {@link EntityManager} and {@link Key}. - * + * * @param em must not be {@literal null}. * @param key may be {@literal null}. * @param extractor must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. * @return */ - public static QueryLookupStrategy create(EntityManager em, Key key, QueryExtractor extractor, + public static QueryLookupStrategy create(EntityManager em, @Nullable Key key, QueryExtractor extractor, EvaluationContextProvider evaluationContextProvider) { Assert.notNull(em, "EntityManager must not be null!"); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java index 264c27aa0..d6bc7ddda 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaQueryMethod.java @@ -41,6 +41,7 @@ import org.springframework.data.repository.core.RepositoryMetadata; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -64,7 +65,7 @@ public class JpaQueryMethod extends QueryMethod { static { - Set> types = new HashSet>(); + Set> types = new HashSet<>(); types.add(byte[].class); types.add(Byte[].class); types.add(char[].class); @@ -76,14 +77,15 @@ public class JpaQueryMethod extends QueryMethod { private final QueryExtractor extractor; private final Method method; - private StoredProcedureAttributes storedProcedureAttributes; + private @Nullable StoredProcedureAttributes storedProcedureAttributes; /** * Creates a {@link JpaQueryMethod}. * * @param method must not be {@literal null} - * @param extractor must not be {@literal null} * @param metadata must not be {@literal null} + * @param factory must not be {@literal null} + * @param extractor must not be {@literal null} */ public JpaQueryMethod(Method method, RepositoryMetadata metadata, ProjectionFactory factory, QueryExtractor extractor) { @@ -115,8 +117,9 @@ public class JpaQueryMethod extends QueryMethod { continue; } - if (!annotatedQuery.contains(String.format(":%s", parameter.getName().get())) - && !annotatedQuery.contains(String.format("#%s", parameter.getName().get()))) { + if (StringUtils.isEmpty(annotatedQuery) + || !annotatedQuery.contains(String.format(":%s", parameter.getName().get())) + && !annotatedQuery.contains(String.format("#%s", parameter.getName().get()))) { throw new IllegalStateException( String.format("Using named parameters for method %s but parameter '%s' not found in annotated query '%s'!", method, parameter.getName(), annotatedQuery)); @@ -152,7 +155,7 @@ public class JpaQueryMethod extends QueryMethod { */ List getHints() { - List result = new ArrayList(); + List result = new ArrayList<>(); QueryHints hints = AnnotatedElementUtils.findMergedAnnotation(method, QueryHints.class); if (hints != null) { @@ -167,6 +170,7 @@ public class JpaQueryMethod extends QueryMethod { * * @return */ + @Nullable LockModeType getLockModeType() { return (LockModeType) Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, Lock.class)) // @@ -180,6 +184,7 @@ public class JpaQueryMethod extends QueryMethod { * @return * @since 1.6 */ + @Nullable JpaEntityGraph getEntityGraph() { EntityGraph annotation = AnnotatedElementUtils.findMergedAnnotation(method, EntityGraph.class); @@ -224,18 +229,39 @@ public class JpaQueryMethod extends QueryMethod { * * @return */ + @Nullable String getAnnotatedQuery() { String query = getAnnotationValue("value", String.class); return StringUtils.hasText(query) ? query : null; } + /** + * Returns the required query string declared in a {@link Query} annotation or throws {@link IllegalStateException} if + * neither the annotation found nor the attribute was specified. + * + * @return + * @throws IllegalStateException if no {@link Query} annotation is present or the query is empty. + * @since 2.0 + */ + String getRequiredAnnotatedQuery() throws IllegalStateException { + + String query = getAnnotatedQuery(); + + if (query != null) { + return query; + } + + throw new IllegalStateException(String.format("No annotated query found for query method %s!", getName())); + } + /** * Returns the countQuery string declared in a {@link Query} annotation or {@literal null} if neither the annotation * found nor the attribute was specified. * * @return */ + @Nullable String getCountQuery() { String countQuery = getAnnotationValue("countQuery", String.class); @@ -249,6 +275,7 @@ public class JpaQueryMethod extends QueryMethod { * @return * @since 1.6 */ + @Nullable String getCountQueryProjection() { String countProjection = getAnnotationValue("countProjection", String.class); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java b/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java index ac6b0653d..28306002d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/JpaResultConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2014 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -24,12 +24,14 @@ import java.sql.SQLException; import org.springframework.core.convert.converter.Converter; import org.springframework.dao.CleanupFailureDataAccessException; import org.springframework.dao.DataRetrievalFailureException; +import org.springframework.lang.Nullable; import org.springframework.util.StreamUtils; /** * Container for additional JPA result {@link Converter}s. * * @author Thomas Darimont + * @author Mark Paluch * @since 1.6 */ final class JpaResultConverters { @@ -48,8 +50,9 @@ final class JpaResultConverters { INSTANCE; + @Nullable @Override - public byte[] convert(Blob source) { + public byte[] convert(@Nullable Blob source) { if (source == null) { return null; @@ -67,9 +70,7 @@ final class JpaResultConverters { return baos.toByteArray(); } - } catch (SQLException e) { - throw new DataRetrievalFailureException("Couldn't retrieve data from blob.", e); - } catch (IOException e) { + } catch (SQLException | IOException e) { throw new DataRetrievalFailureException("Couldn't retrieve data from blob.", e); } finally { if (blobStream != null) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java index 860c6aed7..e86ba84ea 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/NamedQuery.java @@ -25,12 +25,14 @@ import org.springframework.data.jpa.provider.QueryExtractor; import org.springframework.data.repository.query.Parameters; import org.springframework.data.repository.query.QueryCreationException; import org.springframework.data.repository.query.RepositoryQuery; +import org.springframework.lang.Nullable; /** * Implementation of {@link RepositoryQuery} based on {@link javax.persistence.NamedQuery}s. * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ final class NamedQuery extends AbstractJpaQuery { @@ -43,7 +45,7 @@ final class NamedQuery extends AbstractJpaQuery { private final String queryName; private final String countQueryName; - private final String countProjection; + private final @Nullable String countProjection; private final QueryExtractor extractor; private final boolean namedCountQueryIsPresent; @@ -112,6 +114,7 @@ final class NamedQuery extends AbstractJpaQuery { * @param method * @return */ + @Nullable public static RepositoryQuery lookupFrom(JpaQueryMethod method, EntityManager em) { final String queryName = method.getNamedQueryName(); @@ -157,6 +160,11 @@ final class NamedQuery extends AbstractJpaQuery { } else { Query query = createQuery(values); String queryString = extractor.extractQueryString(query); + + if (queryString == null) { + throw new IllegalStateException(String.format("Cannot extract query string for query %s is null!", query)); + } + countQuery = em.createQuery(QueryUtils.createCountQueryFor(queryString, countProjection), Long.class); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java index 31ea8a77a..1225c89ab 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinder.java @@ -63,7 +63,7 @@ public class ParameterBinder { * Binds the parameters to the given query and applies special parameter types (e.g. pagination). * * @param query must not be {@literal null}. - * @param values values of method parameters to be assigned to the query parame + * @param values values of method parameters to be assigned to the query parameters. */ Query bindAndPrepare(Query query, Object[] values) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java index 1ed01b4e0..a83724159 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterBinderFactory.java @@ -18,6 +18,7 @@ package org.springframework.data.jpa.repository.query; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Objects; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.jpa.repository.query.ParameterMetadataProvider.ParameterMetadata; @@ -25,6 +26,7 @@ import org.springframework.data.jpa.repository.query.StringQuery.ParameterBindin import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.util.StreamUtils; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -123,7 +125,7 @@ class ParameterBinderFactory { return createSetters(null, parameterBindings, factories); } - private static Iterable createSetters(String queryString, + private static Iterable createSetters(@Nullable String queryString, List parameterBindings, QueryParameterSetterFactory... strategies) { return parameterBindings.stream() // @@ -132,11 +134,11 @@ class ParameterBinderFactory { } private static QueryParameterSetter createQueryParameterSetter(ParameterBinding binding, - QueryParameterSetterFactory[] strategies, String queryString) { + QueryParameterSetterFactory[] strategies, @Nullable String queryString) { return Arrays.stream(strategies)// .map(it -> it.create(binding, queryString))// - .filter(it -> it != null)// + .filter(Objects::nonNull)// .findFirst()// .orElse(QueryParameterSetter.NOOP); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java index 5ee7c1a6a..d1149839f 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/ParameterMetadataProvider.java @@ -32,6 +32,7 @@ import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.parser.Part; import org.springframework.data.repository.query.parser.Part.Type; import org.springframework.expression.Expression; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; import org.springframework.util.ObjectUtils; @@ -49,7 +50,7 @@ class ParameterMetadataProvider { private final CriteriaBuilder builder; private final Iterator parameters; private final List> expressions; - private final Iterator bindableParameterValues; + private final @Nullable Iterator bindableParameterValues; private final PersistenceProvider persistenceProvider; /** @@ -88,7 +89,7 @@ class ParameterMetadataProvider { * @param parameters must not be {@literal null}. * @param provider must not be {@literal null}. */ - private ParameterMetadataProvider(CriteriaBuilder builder, Iterator bindableParameterValues, + private ParameterMetadataProvider(CriteriaBuilder builder, @Nullable Iterator bindableParameterValues, Parameters parameters, PersistenceProvider provider) { Assert.notNull(builder, "CriteriaBuilder must not be null!"); @@ -190,7 +191,8 @@ class ParameterMetadataProvider { * @param value * @param provider */ - public ParameterMetadata(ParameterExpression expression, Type type, Object value, PersistenceProvider provider) { + public ParameterMetadata(ParameterExpression expression, Type type, @Nullable Object value, + PersistenceProvider provider) { this.expression = expression; this.persistenceProvider = provider; @@ -221,6 +223,7 @@ class ParameterMetadataProvider { * @param value must not be {@literal null}. * @return */ + @Nullable public Object prepare(Object value) { Assert.notNull(value, "Value must not be null!"); @@ -254,7 +257,8 @@ class ParameterMetadataProvider { * @param value * @return */ - private static Collection toCollection(Object value) { + @Nullable + private static Collection toCollection(@Nullable Object value) { if (value == null) { return null; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java index 3ddb0b9fc..963a1b12d 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/PartTreeJpaQuery.java @@ -33,6 +33,7 @@ import org.springframework.data.repository.query.ParametersParameterAccessor; import org.springframework.data.repository.query.ResultProcessor; import org.springframework.data.repository.query.ReturnedType; import org.springframework.data.repository.query.parser.PartTree; +import org.springframework.lang.Nullable; /** * A {@link AbstractJpaQuery} implementation based on a {@link PartTree}. @@ -41,6 +42,7 @@ import org.springframework.data.repository.query.parser.PartTree; * @author Thomas Darimont * @author Christoph Strobl * @author Jens Schauder + * @author Mark Paluch */ public class PartTreeJpaQuery extends AbstractJpaQuery { @@ -116,9 +118,9 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { */ private class QueryPreparer { - private final CriteriaQuery cachedCriteriaQuery; - private final ParameterBinder cachedParameterBinder; - private final List> expressions; + private final @Nullable CriteriaQuery cachedCriteriaQuery; + private final @Nullable ParameterBinder cachedParameterBinder; + private final @Nullable List> expressions; private final PersistenceProvider persistenceProvider; QueryPreparer(PersistenceProvider persistenceProvider, boolean recreateQueries) { @@ -156,6 +158,10 @@ public class PartTreeJpaQuery extends AbstractJpaQuery { TypedQuery jpaQuery = createQuery(criteriaQuery); + if (parameterBinder == null) { + throw new IllegalStateException("ParameterBinder is null!"); + } + return restrictMaxResultsIfNecessary(invokeBinding(parameterBinder, jpaQuery, values)); } diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java index cccb49d45..e1773ee32 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetter.java @@ -23,6 +23,7 @@ import javax.persistence.Query; import javax.persistence.TemporalType; import javax.persistence.criteria.ParameterExpression; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -30,6 +31,7 @@ import org.springframework.util.Assert; * {@literal Query.setParameter}. * * @author Jens Schauder + * @author Mark Paluch * @since 2.0 */ interface QueryParameterSetter { @@ -46,17 +48,17 @@ interface QueryParameterSetter { private final Function valueExtractor; private final Parameter parameter; - private final TemporalType temporalType; + private final @Nullable TemporalType temporalType; private final boolean lenient; /** * @param valueExtractor must not be {@literal null}. * @param parameter must not be {@literal null}. - * @param temporalType must not be {@literal null}. + * @param temporalType may be {@literal null}. * @param lenient must not be {@literal null}. */ NamedOrIndexedQueryParameterSetter(Function valueExtractor, Parameter parameter, - TemporalType temporalType, boolean lenient) { + @Nullable TemporalType temporalType, boolean lenient) { Assert.notNull(valueExtractor, "ValueExtractor must not be null!"); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java index c9ccd6f14..6b02a50ea 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryParameterSetterFactory.java @@ -31,6 +31,7 @@ import org.springframework.data.repository.query.Parameters; import org.springframework.expression.EvaluationContext; import org.springframework.expression.Expression; import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -39,15 +40,17 @@ import org.springframework.util.Assert; * * @author Jens Schauder * @author Oliver Gierke + * @author Mark Paluch * @since 2.0 */ abstract class QueryParameterSetterFactory { - abstract QueryParameterSetter create(ParameterBinding binding, String queryString); + @Nullable + abstract QueryParameterSetter create(ParameterBinding binding, @Nullable String queryString); /** * Creates a new {@link QueryParameterSetterFactory} for the given {@link JpaParameters}. - * + * * @param parameters must not be {@literal null}. * @return a basic {@link QueryParameterSetterFactory} that can handle named and index parameters. */ @@ -61,7 +64,7 @@ abstract class QueryParameterSetterFactory { /** * Creates a new {@link QueryParameterSetterFactory} using the given {@link JpaParameters} and * {@link ParameterMetadata}. - * + * * @param parameters must not be {@literal null}. * @param metadata must not be {@literal null}. * @return a {@link QueryParameterSetterFactory} for criteria Queries. @@ -77,7 +80,7 @@ abstract class QueryParameterSetterFactory { /** * Creates a new {@link QueryParameterSetterFactory} for the given {@link SpelExpressionParser}, * {@link EvaluationContextProvider} and {@link Parameters}. - * + * * @param parser must not be {@literal null}. * @param evaluationContextProvider must not be {@literal null}. * @param parameters must not be {@literal null}. @@ -104,10 +107,10 @@ abstract class QueryParameterSetterFactory { * @param lenient when true certain exceptions thrown when setting the query parameters get ignored. */ private static QueryParameterSetter createSetter(Function valueExtractor, ParameterBinding binding, - JpaParameter parameter, boolean lenient) { + @Nullable JpaParameter parameter, boolean lenient) { TemporalType temporalType = parameter != null && parameter.isTemporalParameter() // - ? parameter.getTemporalType() // + ? parameter.getRequiredTemporalType() // : null; return new NamedOrIndexedQueryParameterSetter(valueExtractor.andThen(binding::prepare), @@ -148,8 +151,9 @@ abstract class QueryParameterSetterFactory { * (non-Javadoc) * @see org.springframework.data.jpa.repository.query.QueryParameterSetterFactory#create(org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding, java.lang.String) */ + @Nullable @Override - public QueryParameterSetter create(ParameterBinding binding, String queryString) { + public QueryParameterSetter create(ParameterBinding binding, @Nullable String queryString) { if (!binding.isExpression()) { return null; @@ -162,11 +166,12 @@ abstract class QueryParameterSetterFactory { /** * Evaluates the given {@link Expression} against the given values. - * + * * @param expression must not be {@literal null}. * @param values must not be {@literal null}. * @return the result of the evaluation. */ + @Nullable private Object evaluateExpression(Expression expression, Object[] values) { EvaluationContext context = evaluationContextProvider.getEvaluationContext(parameters, values); @@ -200,23 +205,24 @@ abstract class QueryParameterSetterFactory { * @see org.springframework.data.jpa.repository.query.QueryParameterSetterFactory#create(org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding, java.lang.String) */ @Override - public QueryParameterSetter create(ParameterBinding binding, String queryString) { + public QueryParameterSetter create(ParameterBinding binding, @Nullable String queryString) { Assert.notNull(binding, "Binding must not be null."); JpaParameter parameter = QueryUtils.hasNamedParameter(queryString) // ? findParameterForBinding(binding) // - : parameters.getBindableParameter(binding.getPosition() - 1); + : parameters.getBindableParameter(binding.getRequiredPosition() - 1); return parameter == null // ? QueryParameterSetter.NOOP // : createSetter(values -> getValue(values, parameter), binding, parameter, false); } + @Nullable private JpaParameter findParameterForBinding(ParameterBinding binding) { return parameters.getBindableParameters().stream() // - .filter(candidate -> binding.getName().equals(getName(candidate))) // + .filter(candidate -> binding.getRequiredName().equals(getName(candidate))) // .findFirst().orElse(null); } @@ -231,7 +237,7 @@ abstract class QueryParameterSetterFactory { /** * {@link QueryParameterSetterFactory} - * + * * @author Jens Schauder * @author Oliver Gierke */ @@ -243,7 +249,7 @@ abstract class QueryParameterSetterFactory { /** * Creates a new {@link QueryParameterSetterFactory} from the given {@link JpaParameters} and * {@link ParameterMetadata}. - * + * * @param parameters must not be {@literal null}. * @param metadata must not be {@literal null}. */ @@ -261,21 +267,22 @@ abstract class QueryParameterSetterFactory { * @see org.springframework.data.jpa.repository.query.QueryParameterSetterFactory#create(org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding, java.lang.String) */ @Override - public QueryParameterSetter create(ParameterBinding binding, String queryString) { + public QueryParameterSetter create(ParameterBinding binding, @Nullable String queryString) { - ParameterMetadata metadata = expressions.get(binding.getPosition() - 1); + ParameterMetadata metadata = expressions.get(binding.getRequiredPosition() - 1); if (metadata.isIsNullParameter()) { return QueryParameterSetter.NOOP; } - JpaParameter parameter = parameters.getBindableParameter(binding.getPosition() - 1); - TemporalType temporalType = parameter.isTemporalParameter() ? parameter.getTemporalType() : null; + JpaParameter parameter = parameters.getBindableParameter(binding.getRequiredPosition() - 1); + TemporalType temporalType = parameter.isTemporalParameter() ? parameter.getRequiredTemporalType() : null; return new NamedOrIndexedQueryParameterSetter(values -> getAndPrepare(parameter, metadata, values), metadata.getExpression(), temporalType, false); } + @Nullable private Object getAndPrepare(JpaParameter parameter, ParameterMetadata metadata, Object[] values) { JpaParametersParameterAccessor accessor = new JpaParametersParameterAccessor(parameters, values); @@ -287,17 +294,17 @@ abstract class QueryParameterSetterFactory { private static class ParameterImpl implements javax.persistence.Parameter { private final Class parameterType; - private final String name; - private final Integer position; + private final @Nullable String name; + private final @Nullable Integer position; /** * Creates a new {@link ParameterImpl} for the given {@link JpaParameter} and {@link ParameterBinding}. - * + * * @param parameter can be {@literal null}. * @param binding must not be {@literal null}. * @return a {@link javax.persistence.Parameter} object based on the information from the arguments. */ - static javax.persistence.Parameter of(JpaParameter parameter, ParameterBinding binding) { + static javax.persistence.Parameter of(@Nullable JpaParameter parameter, ParameterBinding binding) { Class type = parameter == null ? Object.class : parameter.getType(); @@ -306,37 +313,39 @@ abstract class QueryParameterSetterFactory { /** * Creates a new {@link ParameterImpl} for the given name, position and parameter type. - * + * * @param parameterType must not be {@literal null}. * @param name can be {@literal null}. * @param position can be {@literal null}. */ - private ParameterImpl(Class parameterType, String name, Integer position) { + private ParameterImpl(Class parameterType, @Nullable String name, @Nullable Integer position) { this.name = name; this.position = position; this.parameterType = parameterType; } - /* + /* * (non-Javadoc) * @see javax.persistence.Parameter#getName() */ + @Nullable @Override public String getName() { return name; } - /* + /* * (non-Javadoc) * @see javax.persistence.Parameter#getPosition() */ + @Nullable @Override public Integer getPosition() { return position; } - /* + /* * (non-Javadoc) * @see javax.persistence.Parameter#getParameterType() */ @@ -345,7 +354,8 @@ abstract class QueryParameterSetterFactory { return parameterType; } - private static String getName(JpaParameter parameter, ParameterBinding binding) { + @Nullable + private static String getName(@Nullable JpaParameter parameter, ParameterBinding binding) { if (parameter == null) { return binding.getName(); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java index f7541c553..aa6dd1bbb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/QueryUtils.java @@ -57,6 +57,7 @@ import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.JpaSort.JpaOrder; import org.springframework.data.mapping.PropertyPath; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -224,14 +225,14 @@ public abstract class QueryUtils { * * @param query the query string to which sorting is applied. Must not be {@literal null} or empty. * @param sort the sort specification to apply. - * @param alias the alias to be used in the order by clause. Must not be {@literal null} or empty. + * @param alias the alias to be used in the order by clause. May be {@literal null} or empty. * @return the modified query string. */ - public static String applySorting(String query, Sort sort, String alias) { + public static String applySorting(String query, Sort sort, @Nullable String alias) { Assert.hasText(query, "Query must not be null or empty!"); - if (null == sort || !sort.iterator().hasNext()) { + if (sort.isUnsorted()) { return query; } @@ -264,7 +265,8 @@ public abstract class QueryUtils { * @param order the order object to build the clause for. * @return */ - private static String getOrderClause(Set joinAliases, Set functionAlias, String alias, Order order) { + private static String getOrderClause(Set joinAliases, Set functionAlias, @Nullable String alias, + Order order) { String property = order.getProperty(); @@ -345,6 +347,7 @@ public abstract class QueryUtils { * @param query * @return */ + @Nullable public static String detectAlias(String query) { Matcher matcher = ALIAS_MATCH.matcher(query); @@ -421,7 +424,7 @@ public abstract class QueryUtils { * @return * @since 1.6 */ - public static String createCountQueryFor(String originalQuery, String countProjection) { + public static String createCountQueryFor(String originalQuery, @Nullable String countProjection) { Assert.hasText(originalQuery, "OriginalQuery must not be null or empty!"); @@ -472,7 +475,7 @@ public abstract class QueryUtils { * @param query can be {@literal null} or empty. * @return */ - public static boolean hasNamedParameter(String query) { + public static boolean hasNamedParameter(@Nullable String query) { return StringUtils.hasText(query) && NAMED_PARAMETER.matcher(query).find(); } @@ -488,7 +491,7 @@ public abstract class QueryUtils { List orders = new ArrayList(); - if (sort == null) { + if (sort.isUnsorted()) { return orders; } @@ -556,7 +559,7 @@ public abstract class QueryUtils { @SuppressWarnings("unchecked") static Expression toExpressionRecursively(From from, PropertyPath property) { - Bindable propertyPathModel = null; + Bindable propertyPathModel; Bindable model = from.getModel(); String segment = property.getSegment(); @@ -584,11 +587,11 @@ public abstract class QueryUtils { * Returns whether the given {@code propertyPathModel} requires the creation of a join. This is the case if we find a * non-optional association. * - * @param propertyPathModel must not be {@literal null}. + * @param propertyPathModel may be {@literal null}. * @param forPluralAttribute * @return */ - private static boolean requiresJoin(Bindable propertyPathModel, boolean forPluralAttribute) { + private static boolean requiresJoin(@Nullable Bindable propertyPathModel, boolean forPluralAttribute) { if (propertyPathModel == null && forPluralAttribute) { return true; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java index 2ee943f9a..22325e278 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/SimpleJpaQuery.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2013 the original author or authors. + * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -29,6 +29,7 @@ import org.springframework.expression.spel.standard.SpelExpressionParser; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { @@ -42,7 +43,7 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { */ public SimpleJpaQuery(JpaQueryMethod method, EntityManager em, EvaluationContextProvider evaluationContextProvider, SpelExpressionParser parser) { - this(method, em, method.getAnnotatedQuery(), evaluationContextProvider, parser); + this(method, em, method.getRequiredAnnotatedQuery(), evaluationContextProvider, parser); } /** @@ -73,7 +74,7 @@ final class SimpleJpaQuery extends AbstractStringBasedJpaQuery { * @param query * @param em */ - private final void validateQuery(String query, String errorMessage) { + private void validateQuery(String query, String errorMessage) { if (getQueryMethod().isProcedureQuery()) { return; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java index 751c71f1c..4976b3ac3 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributeSource.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -25,6 +25,7 @@ import javax.persistence.NamedStoredProcedureQuery; import javax.persistence.StoredProcedureParameter; import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -34,6 +35,7 @@ import org.springframework.util.StringUtils; * @author Thomas Darimont * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch * @since 1.6 */ enum StoredProcedureAttributeSource { @@ -157,6 +159,7 @@ enum StoredProcedureAttributeSource { * @param procedure must not be {@literal null}. * @return */ + @Nullable private NamedStoredProcedureQuery tryFindAnnotatedNamedStoredProcedureQuery(Method method, JpaEntityMetadata entityMetadata, Procedure procedure) { diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java index 2c6c0f931..2e5453057 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureAttributes.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2015 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -17,6 +17,7 @@ package org.springframework.data.jpa.repository.query; import javax.persistence.StoredProcedureQuery; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -25,6 +26,7 @@ import org.springframework.util.StringUtils; * * @author Thomas Darimont * @author Oliver Gierke + * @author Mark Paluch * @since 1.6 */ class StoredProcedureAttributes { @@ -45,8 +47,8 @@ class StoredProcedureAttributes { * @param outputParameterIndex must not be {@literal null} * @param outputParameterType */ - public StoredProcedureAttributes(String procedureName, String outputParameterName, Class outputParameterType, - boolean namedStoredProcedure) { + public StoredProcedureAttributes(String procedureName, @Nullable String outputParameterName, + Class outputParameterType, boolean namedStoredProcedure) { Assert.notNull(procedureName, "ProcedureName must not be null!"); Assert.notNull(outputParameterType, "OutputParameterType must not be null!"); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java index 03f704c1f..7e48b1cfe 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StoredProcedureJpaQuery.java @@ -24,6 +24,7 @@ import javax.persistence.TypedQuery; import org.springframework.data.jpa.repository.query.JpaParameters.JpaParameter; import org.springframework.data.repository.query.Parameter; import org.springframework.data.repository.query.QueryMethod; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.StringUtils; @@ -35,6 +36,7 @@ import org.springframework.util.StringUtils; * @author Oliver Gierke * @author Christoph Strobl * @author Jens Schauder + * @author Mark Paluch * @since 1.6 */ class StoredProcedureJpaQuery extends AbstractJpaQuery { @@ -96,7 +98,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { */ @Override protected TypedQuery doCreateCountQuery(Object[] values) { - return null; + throw new UnsupportedOperationException("StoredProcedureQuery does not support count queries!"); } /** @@ -104,6 +106,7 @@ class StoredProcedureJpaQuery extends AbstractJpaQuery { * * @param storedProcedureQuery must not be {@literal null}. */ + @Nullable Object extractOutputValue(StoredProcedureQuery storedProcedureQuery) { Assert.notNull(storedProcedureQuery, "StoredProcedureQuery must not be null!"); diff --git a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java b/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java index d76c0ec77..26af01449 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/StringQuery.java @@ -27,6 +27,7 @@ import java.util.regex.Matcher; import java.util.regex.Pattern; import org.springframework.data.repository.query.parser.Part.Type; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; import org.springframework.util.StringUtils; @@ -47,7 +48,7 @@ class StringQuery { private final String query; private final List bindings; - private final String alias; + private final @Nullable String alias; private final boolean hasConstructorExpression; /** @@ -83,7 +84,7 @@ class StringQuery { /** * Returns the query string. */ - public String getQueryString() { + String getQueryString() { return query; } @@ -92,6 +93,7 @@ class StringQuery { * * @return the alias */ + @Nullable String getAlias() { return alias; } @@ -287,9 +289,9 @@ class StringQuery { // character, while = does not. LIKE("like "), IN("in "), AS_IS(null); - private final String keyword; + private final @Nullable String keyword; - ParameterBindingType(String keyword) { + ParameterBindingType(@Nullable String keyword) { this.keyword = keyword; } @@ -299,6 +301,7 @@ class StringQuery { * * @return the keyword */ + @Nullable public String getKeyword() { return keyword; } @@ -331,9 +334,9 @@ class StringQuery { */ static class ParameterBinding { - private final String name; - private final String expression; - private final Integer position; + private final @Nullable String name; + private final @Nullable String expression; + private final @Nullable Integer position; /** * Creates a new {@link ParameterBinding} for the parameter with the given position. @@ -352,7 +355,7 @@ class StringQuery { * @param position of the parameter may be {@literal null}. * @param expression the expression to apply to any value for this parameter. */ - ParameterBinding(String name, Integer position, String expression) { + ParameterBinding(@Nullable String name, @Nullable Integer position, @Nullable String expression) { if (name == null) { Assert.notNull(position, "Position must not be null!"); @@ -371,7 +374,7 @@ class StringQuery { * Returns whether the binding has the given name. Will always be {@literal false} in case the * {@link ParameterBinding} has been set up from a position. */ - boolean hasName(String name) { + boolean hasName(@Nullable String name) { return this.position == null && this.name != null && this.name.equals(name); } @@ -379,24 +382,58 @@ class StringQuery { * Returns whether the binding has the given position. Will always be {@literal false} in case the * {@link ParameterBinding} has been set up from a name. */ - boolean hasPosition(Integer position) { + boolean hasPosition(@Nullable Integer position) { return position != null && this.name == null && position.equals(this.position); } /** * @return the name */ + @Nullable public String getName() { return name; } + /** + * @return the name + * @throws IllegalStateException if the name is not available. + * @since 2.0 + */ + String getRequiredName() throws IllegalStateException { + + String name = getName(); + + if (name != null) { + return name; + } + + throw new IllegalStateException(String.format("Required name for %s not available!", this)); + } + /** * @return the position */ + @Nullable Integer getPosition() { return position; } + /** + * @return the position + * @throws IllegalStateException if the position is not available. + * @since 2.0 + */ + int getRequiredPosition() throws IllegalStateException { + + Integer position = getPosition(); + + if (position != null) { + return position; + } + + throw new IllegalStateException(String.format("Required position for %s not available!", this)); + } + /** * @return {@literal true} if this parameter binding is a synthetic SpEL expression. */ @@ -450,10 +487,12 @@ class StringQuery { /** * @param valueToBind value to prepare */ - public Object prepare(Object valueToBind) { + @Nullable + public Object prepare(@Nullable Object valueToBind) { return valueToBind; } + @Nullable public String getExpression() { return expression; } @@ -470,14 +509,14 @@ class StringQuery { /** * Creates a new {@link InParameterBinding} for the parameter with the given name. */ - InParameterBinding(String name, String expression) { + InParameterBinding(String name, @Nullable String expression) { super(name, null, expression); } /** * Creates a new {@link InParameterBinding} for the parameter with the given position. */ - InParameterBinding(int position, String expression) { + InParameterBinding(int position, @Nullable String expression) { super(null, position, expression); } @@ -486,7 +525,7 @@ class StringQuery { * @see org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding#prepare(java.lang.Object) */ @Override - public Object prepare(Object value) { + public Object prepare(@Nullable Object value) { if (!ObjectUtils.isArray(value)) { return value; @@ -535,7 +574,7 @@ class StringQuery { * @param type must not be {@literal null}. * @param expression may be {@literal null}. */ - LikeParameterBinding(String name, Type type, String expression) { + LikeParameterBinding(String name, Type type, @Nullable String expression) { super(name, null, expression); @@ -565,7 +604,7 @@ class StringQuery { * @param type must not be {@literal null}. * @param expression may be {@literal null}. */ - LikeParameterBinding(int position, Type type, String expression) { + LikeParameterBinding(int position, Type type, @Nullable String expression) { super(null, position, expression); @@ -592,8 +631,9 @@ class StringQuery { * * @param keyword */ + @Nullable @Override - public Object prepare(Object value) { + public Object prepare(@Nullable Object value) { if (value == null) { return null; diff --git a/src/main/java/org/springframework/data/jpa/repository/query/package-info.java b/src/main/java/org/springframework/data/jpa/repository/query/package-info.java index 5d82778a6..9e269c303 100644 --- a/src/main/java/org/springframework/data/jpa/repository/query/package-info.java +++ b/src/main/java/org/springframework/data/jpa/repository/query/package-info.java @@ -1,5 +1,7 @@ /** * Query implementation to exectue queries against JPA. */ +@NonNullApi package org.springframework.data.jpa.repository.query; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java index a53966e53..65b589606 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadata.java @@ -22,6 +22,7 @@ import java.util.Optional; import javax.persistence.LockModeType; import org.springframework.data.jpa.repository.EntityGraph; +import org.springframework.lang.Nullable; /** * Interface to abstract {@link CrudMethodMetadata} that provide the {@link LockModeType} to be used for query @@ -30,6 +31,7 @@ import org.springframework.data.jpa.repository.EntityGraph; * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ public interface CrudMethodMetadata { @@ -38,6 +40,7 @@ public interface CrudMethodMetadata { * * @return */ + @Nullable LockModeType getLockModeType(); /** diff --git a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java index c2c99f5f8..63eb3369a 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/CrudMethodMetadataPostProcessor.java @@ -39,6 +39,7 @@ import org.springframework.data.jpa.repository.Lock; import org.springframework.data.jpa.repository.QueryHints; import org.springframework.data.repository.core.RepositoryInformation; import org.springframework.data.repository.core.support.RepositoryProxyPostProcessor; +import org.springframework.lang.Nullable; import org.springframework.transaction.support.TransactionSynchronizationManager; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -51,10 +52,11 @@ import org.springframework.util.ClassUtils; * @author Oliver Gierke * @author Thomas Darimont * @author Christoph Strobl + * @author Mark Paluch */ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, BeanClassLoaderAware { - private ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); + private @Nullable ClassLoader classLoader = ClassUtils.getDefaultClassLoader(); /* * (non-Javadoc) @@ -62,8 +64,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B */ @Override public void setBeanClassLoader(ClassLoader classLoader) { - this.classLoader = classLoader == null ? ClassUtils.getDefaultClassLoader() : classLoader; - + this.classLoader = classLoader; } /* @@ -97,7 +98,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B * @author Oliver Gierke * @author Thomas Darimont */ - static enum CrudMethodMetadataPopulatingMethodInterceptor implements MethodInterceptor { + enum CrudMethodMetadataPopulatingMethodInterceptor implements MethodInterceptor { INSTANCE; @@ -146,7 +147,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B */ private static class DefaultCrudMethodMetadata implements CrudMethodMetadata { - private final LockModeType lockModeType; + private final @Nullable LockModeType lockModeType; private final Map queryHints; private final Optional entityGraph; private final Method method; @@ -156,7 +157,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B * * @param method must not be {@literal null}. */ - public DefaultCrudMethodMetadata(Method method) { + DefaultCrudMethodMetadata(Method method) { Assert.notNull(method, "Method must not be null!"); @@ -170,6 +171,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B return Optional.ofNullable(AnnotatedElementUtils.findMergedAnnotation(method, EntityGraph.class)); } + @Nullable private static LockModeType findLockModeType(Method method) { Lock annotation = AnnotatedElementUtils.findMergedAnnotation(method, Lock.class); @@ -201,6 +203,7 @@ class CrudMethodMetadataPostProcessor implements RepositoryProxyPostProcessor, B * (non-Javadoc) * @see org.springframework.data.jpa.repository.support.CrudMethodMetadata#getLockModeType() */ + @Nullable @Override public LockModeType getLockModeType() { return lockModeType; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java index c29fed745..d5cd623fd 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaEntityInformation.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2014 the original author or authors. + * Copyright 2011-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -19,12 +19,14 @@ import javax.persistence.metamodel.SingularAttribute; import org.springframework.data.jpa.repository.query.JpaEntityMetadata; import org.springframework.data.repository.core.EntityInformation; +import org.springframework.lang.Nullable; /** * Extension of {@link EntityInformation} to capture additional JPA specific information about entities. * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ public interface JpaEntityInformation extends EntityInformation, JpaEntityMetadata { @@ -33,8 +35,28 @@ public interface JpaEntityInformation extends EntityInformation, J * * @return */ + @Nullable SingularAttribute getIdAttribute(); + /** + * Returns the required identifier type. + * + * @return the identifier type. + * @throws IllegalArgumentException in case no id type could be obtained. + * @since 2.0 + */ + default SingularAttribute getRequiredIdAttribute() throws IllegalArgumentException { + + SingularAttribute id = getIdAttribute(); + + if (id != null) { + return id; + } + + throw new IllegalArgumentException( + String.format("Could not obtain required identifier attribute for type %s!", getEntityName())); + } + /** * Returns {@literal true} if the entity has a composite id. * @@ -57,5 +79,6 @@ public interface JpaEntityInformation extends EntityInformation, J * @param idAttribute * @return */ + @Nullable Object getCompositeIdAttributeValue(Object id, String idAttribute); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java index 73a83c82c..45cc9bdbb 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaMetamodelEntityInformation.java @@ -19,9 +19,11 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; import java.util.List; +import java.util.Optional; import java.util.Set; import javax.persistence.IdClass; +import javax.persistence.metamodel.Attribute; import javax.persistence.metamodel.EntityType; import javax.persistence.metamodel.IdentifiableType; import javax.persistence.metamodel.ManagedType; @@ -34,6 +36,7 @@ import org.springframework.beans.BeanWrapper; import org.springframework.beans.BeanWrapperImpl; import org.springframework.core.annotation.AnnotationUtils; import org.springframework.data.util.DirectFieldAccessFallbackBeanWrapper; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import org.springframework.util.ClassUtils; @@ -49,9 +52,9 @@ import org.springframework.util.ClassUtils; public class JpaMetamodelEntityInformation extends JpaEntityInformationSupport { private final IdMetadata idMetadata; - private final SingularAttribute versionAttribute; + private final Optional> versionAttribute; private final Metamodel metamodel; - private final String entityName; + private final @Nullable String entityName; /** * Creates a new {@link JpaMetamodelEntityInformation} for the given domain class and {@link Metamodel}. @@ -101,11 +104,11 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu * @return */ @SuppressWarnings("unchecked") - private static SingularAttribute findVersionAttribute(IdentifiableType type, + private static Optional> findVersionAttribute(IdentifiableType type, Metamodel metamodel) { try { - return type.getVersion(Object.class); + return Optional.ofNullable(type.getVersion(Object.class)); } catch (IllegalArgumentException o_O) { // Needs workarounds as the method is implemented with a strict type check on e.g. Hibernate < 4.3 } @@ -114,7 +117,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu for (SingularAttribute attribute : attributes) { if (attribute.isVersion()) { - return attribute; + return Optional.of(attribute); } } @@ -125,13 +128,13 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu ManagedType managedSuperType = metamodel.managedType(superType); if (!(managedSuperType instanceof IdentifiableType)) { - return null; + return Optional.empty(); } - return (SingularAttribute) findVersionAttribute((IdentifiableType) managedSuperType, metamodel); + return findVersionAttribute((IdentifiableType) managedSuperType, metamodel); } catch (IllegalArgumentException o_O) { - return null; + return Optional.empty(); } } @@ -139,6 +142,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu * (non-Javadoc) * @see org.springframework.data.repository.core.EntityInformation#getId(java.lang.Object) */ + @Nullable @SuppressWarnings("unchecked") public ID getId(T entity) { @@ -222,14 +226,14 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu @Override public boolean isNew(T entity) { - if (versionAttribute == null || versionAttribute.getJavaType().isPrimitive()) { + if (!versionAttribute.isPresent() + || versionAttribute.map(Attribute::getJavaType).map(Class::isPrimitive).orElse(false)) { return super.isNew(entity); } BeanWrapper wrapper = new DirectFieldAccessFallbackBeanWrapper(entity); - Object versionValue = wrapper.getPropertyValue(versionAttribute.getName()); - return versionValue == null; + return versionAttribute.map(it -> wrapper.getPropertyValue(it.getName()) == null).orElse(true); } /** @@ -242,14 +246,15 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu private final IdentifiableType type; private final Set> attributes; - private Class idType; + private @Nullable Class idType; @SuppressWarnings("unchecked") public IdMetadata(IdentifiableType source) { this.type = source; this.attributes = (Set>) (source.hasSingleIdAttribute() - ? Collections.singleton(source.getId(source.getIdType().getJavaType())) : source.getIdClassAttributes()); + ? Collections.singleton(source.getId(source.getIdType().getJavaType())) + : source.getIdClassAttributes()); } public boolean hasSimpleId() { @@ -265,9 +270,14 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu // lazy initialization of idType field with tolerable benign data-race this.idType = tryExtractIdTypeWithFallbackToIdTypeLookup(); + if (this.idType == null) { + throw new IllegalStateException("Cannot resolve Id type from " + type); + } + return this.idType; } + @Nullable private Class tryExtractIdTypeWithFallbackToIdTypeLookup() { try { @@ -279,6 +289,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu } } + @Nullable private static Class fallbackIdTypeLookup(IdentifiableType type) { IdClass annotation = AnnotationUtils.findAnnotation(type.getJavaType(), IdClass.class); @@ -309,7 +320,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu private final Metamodel metamodel; - public IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class type, Metamodel metamodel) { + IdentifierDerivingDirectFieldAccessFallbackBeanWrapper(Class type, Metamodel metamodel) { super(type); this.metamodel = metamodel; } @@ -320,7 +331,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu */ @Override @SuppressWarnings("unchecked") - public void setPropertyValue(String propertyName, Object value) { + public void setPropertyValue(String propertyName, @Nullable Object value) { if (!isIdentifierDerivationNecessary(value)) { super.setPropertyValue(propertyName, value); @@ -335,7 +346,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu if (!nestedEntityInformation.getJavaType().isAnnotationPresent(IdClass.class)) { Object nestedIdPropertyValue = new DirectFieldAccessFallbackBeanWrapper(value) - .getPropertyValue(nestedEntityInformation.getIdAttribute().getName()); + .getPropertyValue(nestedEntityInformation.getRequiredIdAttribute().getName()); super.setPropertyValue(propertyName, nestedIdPropertyValue); return; } @@ -354,6 +365,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu super.setPropertyValue(propertyName, targetIdClassTypeWrapper.getWrappedInstance()); } + @Nullable private Object extractActualIdPropertyValue(BeanWrapper sourceIdValueWrapper, String idAttributeName) { Object idPropertyValue = sourceIdValueWrapper.getPropertyValue(idAttributeName); @@ -391,7 +403,7 @@ public class JpaMetamodelEntityInformation extends JpaEntityInformationSu * @return {@literal true} if the given value is not {@literal null} and a mapped persistable entity otherwise * {@literal false} */ - private boolean isIdentifierDerivationNecessary(Object value) { + private boolean isIdentifierDerivationNecessary(@Nullable Object value) { if (value == null) { return false; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java index 8bf352f67..4282cb92e 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaPersistableEntityInformation.java @@ -18,12 +18,14 @@ package org.springframework.data.jpa.repository.support; import javax.persistence.metamodel.Metamodel; import org.springframework.data.domain.Persistable; +import org.springframework.lang.Nullable; /** * Extension of {@link JpaMetamodelEntityInformation} that consideres methods of {@link Persistable} to lookup the id. * * @author Oliver Gierke * @author Christoph Strobl + * @author Mark Paluch */ public class JpaPersistableEntityInformation, ID> extends JpaMetamodelEntityInformation { @@ -51,6 +53,7 @@ public class JpaPersistableEntityInformation, ID> * (non-Javadoc) * @see org.springframework.data.jpa.repository.support.JpaMetamodelEntityInformation#getId(java.lang.Object) */ + @Nullable @Override public ID getId(T entity) { return entity.getId(); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java index 86b26e86b..f67262628 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactory.java @@ -33,6 +33,7 @@ import org.springframework.data.repository.core.support.RepositoryFactorySupport import org.springframework.data.repository.query.EvaluationContextProvider; import org.springframework.data.repository.query.QueryLookupStrategy; import org.springframework.data.repository.query.QueryLookupStrategy.Key; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -133,7 +134,7 @@ public class JpaRepositoryFactory extends RepositoryFactorySupport { * @see org.springframework.data.repository.core.support.RepositoryFactorySupport#getQueryLookupStrategy(org.springframework.data.repository.query.QueryLookupStrategy.Key, org.springframework.data.repository.query.EvaluationContextProvider) */ @Override - protected Optional getQueryLookupStrategy(Key key, + protected Optional getQueryLookupStrategy(@Nullable Key key, EvaluationContextProvider evaluationContextProvider) { return Optional.of(JpaQueryLookupStrategy.create(entityManager, key, extractor, evaluationContextProvider)); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java index 615c01693..3e45c82ad 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBean.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -22,6 +22,7 @@ import org.springframework.data.mapping.context.MappingContext; import org.springframework.data.repository.Repository; import org.springframework.data.repository.core.support.RepositoryFactorySupport; import org.springframework.data.repository.core.support.TransactionalRepositoryFactoryBeanSupport; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** @@ -30,12 +31,13 @@ import org.springframework.util.Assert; * * @author Oliver Gierke * @author Eberhard Wolff + * @author Mark Paluch * @param the type of the repository */ public class JpaRepositoryFactoryBean, S, ID> extends TransactionalRepositoryFactoryBeanSupport { - private EntityManager entityManager; + private @Nullable EntityManager entityManager; /** * Creates a new {@link JpaRepositoryFactoryBean} for the given repository interface. @@ -73,6 +75,11 @@ public class JpaRepositoryFactoryBean, S, ID> */ @Override protected RepositoryFactorySupport doCreateRepositoryFactory() { + + if (entityManager == null) { + throw new IllegalStateException("EntityManager must not be null!"); + } + return createRepositoryFactory(entityManager); } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java index 6c14c1bd9..d14859263 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/Querydsl.java @@ -125,7 +125,7 @@ public class Querydsl { */ public JPQLQuery applySorting(Sort sort, JPQLQuery query) { - if (sort == null) { + if (sort.isUnsorted()) { return query; } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java index 08022382e..509bfb591 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslJpaRepository.java @@ -32,6 +32,7 @@ import org.springframework.data.querydsl.QSort; import org.springframework.data.querydsl.QuerydslPredicateExecutor; import org.springframework.data.querydsl.SimpleEntityPathResolver; import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.lang.Nullable; import org.springframework.util.Assert; import com.querydsl.core.NonUniqueResultException; @@ -160,7 +161,7 @@ public class QuerydslJpaRepository extends SimpleJpa final JPQLQuery countQuery = createCountQuery(predicate); JPQLQuery query = querydsl.applyPagination(pageable, createQuery(predicate).select(path)); - return PageableExecutionUtils.getPage(query.fetch(), pageable, () -> countQuery.fetchCount()); + return PageableExecutionUtils.getPage(query.fetch(), pageable, countQuery::fetchCount); } /* @@ -207,13 +208,17 @@ public class QuerydslJpaRepository extends SimpleJpa * @param predicate, can be {@literal null}. * @return the Querydsl count {@link JPQLQuery}. */ - protected JPQLQuery createCountQuery(Predicate... predicate) { + protected JPQLQuery createCountQuery(@Nullable Predicate... predicate) { return doCreateQuery(getQueryHints(), predicate); } - private AbstractJPAQuery doCreateQuery(QueryHints hints, Predicate... predicate) { + private AbstractJPAQuery doCreateQuery(QueryHints hints, @Nullable Predicate... predicate) { - AbstractJPAQuery query = querydsl.createQuery(path).where(predicate); + AbstractJPAQuery query = querydsl.createQuery(path); + + if (predicate != null) { + query = query.where(predicate); + } for (Entry hint : hints) { query.setHint(hint.getKey(), hint.getValue()); diff --git a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java index dfecce34e..d3c0ac209 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/QuerydslRepositorySupport.java @@ -15,6 +15,7 @@ */ package org.springframework.data.jpa.repository.support; +import javax.annotation.Nullable; import javax.annotation.PostConstruct; import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; @@ -42,8 +43,8 @@ public abstract class QuerydslRepositorySupport { private final PathBuilder builder; - private EntityManager entityManager; - private Querydsl querydsl; + private @Nullable EntityManager entityManager; + private @Nullable Querydsl querydsl; /** * Creates a new {@link QuerydslRepositorySupport} instance for the given domain type. @@ -83,6 +84,7 @@ public abstract class QuerydslRepositorySupport { * * @return the entityManager */ + @Nullable protected EntityManager getEntityManager() { return entityManager; } @@ -94,7 +96,7 @@ public abstract class QuerydslRepositorySupport { * @return the Querydsl {@link JPQLQuery}. */ protected JPQLQuery from(EntityPath... paths) { - return querydsl.createQuery(paths); + return getRequiredQuerydsl().createQuery(paths); } /** @@ -104,7 +106,7 @@ public abstract class QuerydslRepositorySupport { * @return */ protected JPQLQuery from(EntityPath path) { - return querydsl.createQuery(path).select(path); + return getRequiredQuerydsl().createQuery(path).select(path); } /** @@ -114,7 +116,7 @@ public abstract class QuerydslRepositorySupport { * @return the Querydsl {@link DeleteClause}. */ protected DeleteClause delete(EntityPath path) { - return new JPADeleteClause(entityManager, path); + return new JPADeleteClause(getRequiredEntityManager(), path); } /** @@ -124,7 +126,7 @@ public abstract class QuerydslRepositorySupport { * @return the Querydsl {@link UpdateClause}. */ protected UpdateClause update(EntityPath path) { - return new JPAUpdateClause(entityManager, path); + return new JPAUpdateClause(getRequiredEntityManager(), path); } /** @@ -143,7 +145,26 @@ public abstract class QuerydslRepositorySupport { * * @return */ + @Nullable protected Querydsl getQuerydsl() { return this.querydsl; } + + private Querydsl getRequiredQuerydsl() { + + if (querydsl == null) { + throw new IllegalStateException("Querydsl is null!"); + } + + return querydsl; + } + + private EntityManager getRequiredEntityManager() { + + if (entityManager == null) { + throw new IllegalStateException("EntityManager is null!"); + } + + return entityManager; + } } diff --git a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java index 531fffb1a..e039b2430 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/SimpleJpaRepository.java @@ -53,6 +53,7 @@ import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.query.QueryUtils; import org.springframework.data.jpa.repository.support.QueryHints.NoHints; import org.springframework.data.repository.support.PageableExecutionUtils; +import org.springframework.lang.Nullable; import org.springframework.stereotype.Repository; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; @@ -79,7 +80,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec private final EntityManager em; private final PersistenceProvider provider; - private CrudMethodMetadata metadata; + private @Nullable CrudMethodMetadata metadata; /** * Creates a new {@link SimpleJpaRepository} to manage objects of the given {@link JpaEntityInformation}. @@ -117,7 +118,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec this.metadata = crudMethodMetadata; } - protected CrudMethodMetadata getRepositoryMethodMetadata() { + protected @Nullable CrudMethodMetadata getRepositoryMethodMetadata() { return metadata; } @@ -301,7 +302,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * @see org.springframework.data.jpa.repository.JpaRepository#findAll() */ public List findAll() { - return getQuery(null, (Sort) null).getResultList(); + return getQuery(null, Sort.unsorted()).getResultList(); } /* @@ -310,7 +311,9 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec */ public List findAllById(Iterable ids) { - if (ids == null || !ids.iterator().hasNext()) { + Assert.notNull(ids, "The given Iterable of Id's must not be null!"); + + if (!ids.iterator().hasNext()) { return Collections.emptyList(); } @@ -326,7 +329,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec } ByIdsSpecification specification = new ByIdsSpecification(entityInformation); - TypedQuery query = getQuery(specification, (Sort) null); + TypedQuery query = getQuery(specification, Sort.unsorted()); return query.setParameter(specification.parameter, ids).getResultList(); } @@ -356,10 +359,10 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findOne(org.springframework.data.jpa.domain.Specification) */ - public Optional findOne(Specification spec) { + public Optional findOne(@Nullable Specification spec) { try { - return Optional.of(getQuery(spec, (Sort) null).getSingleResult()); + return Optional.of(getQuery(spec, Sort.unsorted()).getSingleResult()); } catch (NoResultException e) { return Optional.empty(); } @@ -369,15 +372,15 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification) */ - public List findAll(Specification spec) { - return getQuery(spec, (Sort) null).getResultList(); + public List findAll(@Nullable Specification spec) { + return getQuery(spec, Sort.unsorted()).getResultList(); } /* * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Pageable) */ - public Page findAll(Specification spec, Pageable pageable) { + public Page findAll(@Nullable Specification spec, Pageable pageable) { TypedQuery query = getQuery(spec, pageable); return isUnpaged(pageable) ? new PageImpl(query.getResultList()) @@ -388,7 +391,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#findAll(org.springframework.data.jpa.domain.Specification, org.springframework.data.domain.Sort) */ - public List findAll(Specification spec, Sort sort) { + public List findAll(@Nullable Specification spec, Sort sort) { return getQuery(spec, sort).getResultList(); } @@ -401,7 +404,8 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec try { return Optional - .of(getQuery(new ExampleSpecification(example), example.getProbeType(), (Sort) null).getSingleResult()); + .of(getQuery(new ExampleSpecification(example), example.getProbeType(), Sort.unsorted()) + .getSingleResult()); } catch (NoResultException e) { return Optional.empty(); } @@ -422,7 +426,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec */ @Override public boolean exists(Example example) { - return !getQuery(new ExampleSpecification(example), example.getProbeType(), (Sort) null).getResultList() + return !getQuery(new ExampleSpecification(example), example.getProbeType(), Sort.unsorted()).getResultList() .isEmpty(); } @@ -432,7 +436,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec */ @Override public List findAll(Example example) { - return getQuery(new ExampleSpecification(example), example.getProbeType(), (Sort) null).getResultList(); + return getQuery(new ExampleSpecification(example), example.getProbeType(), Sort.unsorted()).getResultList(); } /* @@ -470,7 +474,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * (non-Javadoc) * @see org.springframework.data.jpa.repository.JpaSpecificationExecutor#count(org.springframework.data.jpa.domain.Specification) */ - public long count(Specification spec) { + public long count(@Nullable Specification spec) { return executeCountQuery(getCountQuery(spec, getDomainClass())); } @@ -509,11 +513,9 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec @Transactional public List saveAll(Iterable entities) { - List result = new ArrayList(); + Assert.notNull(entities, "The given Iterable of entities not be null!"); - if (entities == null) { - return result; - } + List result = new ArrayList(); for (S entity : entities) { result.add(save(entity)); @@ -537,12 +539,12 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * * @param query must not be {@literal null}. * @param spec can be {@literal null}. - * @param pageable can be {@literal null}. + * @param pageable must not be {@literal null}. * @return * @deprecated use {@link #readPage(TypedQuery, Class, Pageable, Specification)} instead */ @Deprecated - protected Page readPage(TypedQuery query, Pageable pageable, Specification spec) { + protected Page readPage(TypedQuery query, Pageable pageable, @Nullable Specification spec) { return readPage(query, getDomainClass(), pageable, spec); } @@ -557,7 +559,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * @return */ protected Page readPage(TypedQuery query, final Class domainClass, Pageable pageable, - final Specification spec) { + @Nullable Specification spec) { if (pageable.isPaged()) { query.setFirstResult((int) pageable.getOffset()); @@ -572,12 +574,12 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * Creates a new {@link TypedQuery} from the given {@link Specification}. * * @param spec can be {@literal null}. - * @param pageable can be {@literal null}. + * @param pageable must not be {@literal null}. * @return */ - protected TypedQuery getQuery(Specification spec, Pageable pageable) { + protected TypedQuery getQuery(@Nullable Specification spec, Pageable pageable) { - Sort sort = pageable == null ? null : pageable.getSort(); + Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted(); return getQuery(spec, getDomainClass(), sort); } @@ -586,12 +588,13 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * * @param spec can be {@literal null}. * @param domainClass must not be {@literal null}. - * @param pageable can be {@literal null}. + * @param pageable must not be {@literal null}. * @return */ - protected TypedQuery getQuery(Specification spec, Class domainClass, Pageable pageable) { + protected TypedQuery getQuery(@Nullable Specification spec, Class domainClass, + Pageable pageable) { - Sort sort = pageable == null ? null : pageable.getSort(); + Sort sort = pageable.isPaged() ? pageable.getSort() : Sort.unsorted(); return getQuery(spec, domainClass, sort); } @@ -599,10 +602,10 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * Creates a {@link TypedQuery} for the given {@link Specification} and {@link Sort}. * * @param spec can be {@literal null}. - * @param sort can be {@literal null}. + * @param sort must not be {@literal null}. * @return */ - protected TypedQuery getQuery(Specification spec, Sort sort) { + protected TypedQuery getQuery(@Nullable Specification spec, Sort sort) { return getQuery(spec, getDomainClass(), sort); } @@ -611,10 +614,10 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * * @param spec can be {@literal null}. * @param domainClass must not be {@literal null}. - * @param sort can be {@literal null}. + * @param sort must not be {@literal null}. * @return */ - protected TypedQuery getQuery(Specification spec, Class domainClass, Sort sort) { + protected TypedQuery getQuery(@Nullable Specification spec, Class domainClass, Sort sort) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(domainClass); @@ -622,7 +625,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec Root root = applySpecificationToCriteria(spec, domainClass, query); query.select(root); - if (sort != null && !sort.isUnsorted()) { + if (sort.isSorted()) { query.orderBy(toOrders(sort, root, builder)); } @@ -637,7 +640,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * @deprecated override {@link #getCountQuery(Specification, Class)} instead */ @Deprecated - protected TypedQuery getCountQuery(Specification spec) { + protected TypedQuery getCountQuery(@Nullable Specification spec) { return getCountQuery(spec, getDomainClass()); } @@ -648,7 +651,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * @param domainClass must not be {@literal null}. * @return */ - protected TypedQuery getCountQuery(Specification spec, Class domainClass) { + protected TypedQuery getCountQuery(@Nullable Specification spec, Class domainClass) { CriteriaBuilder builder = em.getCriteriaBuilder(); CriteriaQuery query = builder.createQuery(Long.class); @@ -675,7 +678,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * @param query must not be {@literal null}. * @return */ - private Root applySpecificationToCriteria(Specification spec, Class domainClass, + private Root applySpecificationToCriteria(@Nullable Specification spec, Class domainClass, CriteriaQuery query) { Assert.notNull(domainClass, "Domain class must not be null!"); @@ -739,7 +742,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec } private static boolean isUnpaged(Pageable pageable) { - return pageable == null || pageable.isUnpaged(); + return pageable.isUnpaged(); } /** @@ -755,9 +758,9 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec private final JpaEntityInformation entityInformation; - ParameterExpression parameter; + @Nullable ParameterExpression parameter; - public ByIdsSpecification(JpaEntityInformation entityInformation) { + ByIdsSpecification(JpaEntityInformation entityInformation) { this.entityInformation = entityInformation; } @@ -790,7 +793,7 @@ public class SimpleJpaRepository implements JpaRepository, JpaSpec * * @param example */ - public ExampleSpecification(Example example) { + ExampleSpecification(Example example) { Assert.notNull(example, "Example must not be null!"); this.example = example; diff --git a/src/main/java/org/springframework/data/jpa/repository/support/package-info.java b/src/main/java/org/springframework/data/jpa/repository/support/package-info.java index 4dab22453..dbb5bdb2c 100644 --- a/src/main/java/org/springframework/data/jpa/repository/support/package-info.java +++ b/src/main/java/org/springframework/data/jpa/repository/support/package-info.java @@ -1,5 +1,7 @@ /** * JPA repository implementations. */ +@NonNullApi package org.springframework.data.jpa.repository.support; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java b/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java index 7d985225c..3faceffa9 100644 --- a/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java +++ b/src/main/java/org/springframework/data/jpa/support/ClasspathScanningPersistenceUnitPostProcessor.java @@ -39,6 +39,7 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternResolver; import org.springframework.core.io.support.ResourcePatternUtils; import org.springframework.core.type.filter.AnnotationTypeFilter; +import org.springframework.lang.Nullable; import org.springframework.orm.jpa.persistenceunit.MutablePersistenceUnitInfo; import org.springframework.orm.jpa.persistenceunit.PersistenceUnitPostProcessor; import org.springframework.util.Assert; @@ -51,6 +52,7 @@ import org.springframework.util.StringUtils; * * @author Oliver Gierke * @author Thomas Darimont + * @author Mark Paluch */ public class ClasspathScanningPersistenceUnitPostProcessor implements PersistenceUnitPostProcessor, ResourceLoaderAware, EnvironmentAware { @@ -62,7 +64,7 @@ public class ClasspathScanningPersistenceUnitPostProcessor private ResourcePatternResolver mappingFileResolver = new PathMatchingResourcePatternResolver(); private Environment environment = new StandardEnvironment(); private ResourceLoader resourceLoader = new DefaultResourceLoader(); - private String mappingFileNamePattern; + private @Nullable String mappingFileNamePattern; /** * Creates a new {@link ClasspathScanningPersistenceUnitPostProcessor} using the given base package as scan base. @@ -125,7 +127,10 @@ public class ClasspathScanningPersistenceUnitPostProcessor for (BeanDefinition definition : provider.findCandidateComponents(basePackage)) { LOG.debug("Registering classpath-scanned entity {} in persistence unit info!", definition.getBeanClassName()); - pui.addManagedClassName(definition.getBeanClassName()); + + if (definition.getBeanClassName() != null) { + pui.addManagedClassName(definition.getBeanClassName()); + } } for (String location : scanForMappingFileLocations()) { diff --git a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java index 48b636967..e57db0b18 100644 --- a/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java +++ b/src/main/java/org/springframework/data/jpa/support/MergingPersistenceUnitManager.java @@ -1,5 +1,5 @@ /* - * Copyright 2011-2013 the original author or authors. + * Copyright 2011-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -46,7 +46,7 @@ public class MergingPersistenceUnitManager extends DefaultPersistenceUnitManager // Invoke normal post processing super.postProcessPersistenceUnitInfo(pui); - PersistenceUnitInfo oldPui = getPersistenceUnitInfo(pui.getPersistenceUnitName()); + PersistenceUnitInfo oldPui = getPersistenceUnitInfo(((PersistenceUnitInfo) pui).getPersistenceUnitName()); if (oldPui != null) { postProcessPersistenceUnitInfo(pui, oldPui); diff --git a/src/main/java/org/springframework/data/jpa/support/package-info.java b/src/main/java/org/springframework/data/jpa/support/package-info.java index 1a2b61924..b37b9ad9a 100644 --- a/src/main/java/org/springframework/data/jpa/support/package-info.java +++ b/src/main/java/org/springframework/data/jpa/support/package-info.java @@ -1,5 +1,7 @@ /** * Various helper classes useful when working with JPA. */ +@NonNullApi package org.springframework.data.jpa.support; +import org.springframework.lang.NonNullApi; diff --git a/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java b/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java index c54a8e499..dea8e71c7 100644 --- a/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java +++ b/src/main/java/org/springframework/data/jpa/util/BeanDefinitionUtils.java @@ -1,5 +1,5 @@ /* - * Copyright 2014-2016 the original author or authors. + * Copyright 2014-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -41,6 +41,7 @@ import org.springframework.util.ClassUtils; * Utility methods to work with {@link BeanDefinition} instances from {@link BeanFactoryPostProcessor}s. * * @author Oliver Gierke + * @author Mark Paluch */ public class BeanDefinitionUtils { @@ -127,8 +128,10 @@ public class BeanDefinitionUtils { if (!EntityManagerFactory.class.getName().equals(definition.getPropertyValues().get("expectedType"))) { return; } - } else if (beanFactory.getType(name) == null - || !EntityManagerFactory.class.isAssignableFrom(beanFactory.getType(name))) { + } + + Class type = beanFactory.getType(name); + if (type == null || !EntityManagerFactory.class.isAssignableFrom(type)) { return; } diff --git a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java b/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java index 4d9bbe820..1b8023a1b 100644 --- a/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java +++ b/src/main/java/org/springframework/data/jpa/util/JpaMetamodel.java @@ -18,6 +18,7 @@ package org.springframework.data.jpa.util; import java.util.Collection; import java.util.Collections; import java.util.HashSet; +import java.util.Optional; import java.util.Set; import javax.persistence.metamodel.ManagedType; @@ -29,12 +30,13 @@ import org.springframework.util.Assert; * Wrapper around the JPA {@link Metamodel} to be able to apply some fixes against bugs in provider implementations. * * @author Oliver Gierke + * @author Mark Paluch */ public class JpaMetamodel { private final Metamodel metamodel; - private Collection> managedTypes; + private Optional>> managedTypes = Optional.empty(); /** * Creates a new {@link JpaMetamodel} for the given JPA {@link Metamodel}. @@ -70,24 +72,23 @@ public class JpaMetamodel { */ private Collection> getManagedTypes() { - if (managedTypes != null) { - return managedTypes; - } + if (!managedTypes.isPresent()) { - Set> managedTypes = metamodel.getManagedTypes(); - Set> types = new HashSet>(managedTypes.size()); + Set> managedTypes = metamodel.getManagedTypes(); + Set> types = new HashSet>(managedTypes.size()); - for (ManagedType managedType : metamodel.getManagedTypes()) { + for (ManagedType managedType : metamodel.getManagedTypes()) { - Class type = managedType.getJavaType(); + Class type = managedType.getJavaType(); - if (type != null) { - types.add(type); + if (type != null) { + types.add(type); + } } + + this.managedTypes = Optional.of(Collections.unmodifiableSet(types)); } - this.managedTypes = Collections.unmodifiableSet(types); - - return this.managedTypes; + return this.managedTypes.get(); } } diff --git a/src/main/java/org/springframework/data/jpa/util/package-info.java b/src/main/java/org/springframework/data/jpa/util/package-info.java new file mode 100644 index 000000000..e374512f9 --- /dev/null +++ b/src/main/java/org/springframework/data/jpa/util/package-info.java @@ -0,0 +1,7 @@ +/** + * Spring Data JPA utilities. + */ +@NonNullApi +package org.springframework.data.jpa.util; + +import org.springframework.lang.NonNullApi; diff --git a/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java b/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java index 75c9a6254..04f269e2e 100644 --- a/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/JpaSortTests.java @@ -35,6 +35,7 @@ import org.springframework.data.jpa.domain.sample.Address_; import org.springframework.data.jpa.domain.sample.MailMessage_; import org.springframework.data.jpa.domain.sample.MailSender_; import org.springframework.data.jpa.domain.sample.User_; +import org.springframework.lang.Nullable; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @@ -52,10 +53,10 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @ContextConfiguration("classpath:infrastructure.xml") public class JpaSortTests { - private static final Attribute NULL_ATTRIBUTE = null; + private static final @Nullable Attribute NULL_ATTRIBUTE = null; private static final Attribute[] EMPTY_ATTRIBUTES = new Attribute[0]; - private static final PluralAttribute NULL_PLURAL_ATTRIBUTE = null; + private static final @Nullable PluralAttribute NULL_PLURAL_ATTRIBUTE = null; private static final PluralAttribute[] EMPTY_PLURAL_ATTRIBUTES = new PluralAttribute[0]; @Test(expected = IllegalArgumentException.class) // DATAJPA-12 diff --git a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java b/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java index 5eef4acce..a7bae73c5 100644 --- a/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java +++ b/src/test/java/org/springframework/data/jpa/domain/sample/AuditableUser.java @@ -15,7 +15,6 @@ */ package org.springframework.data.jpa.domain.sample; -import java.time.Instant; import java.util.HashSet; import java.util.Set; @@ -25,11 +24,12 @@ import javax.persistence.ManyToMany; import javax.persistence.NamedQuery; import org.springframework.data.jpa.domain.AbstractAuditable; +import org.springframework.lang.Nullable; /** * Sample auditable user to demonstrate working with {@code AbstractAuditableEntity}. No declaration of an ID is * necessary. Furthermore no auditing information has to be declared explicitly. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -41,24 +41,25 @@ public class AuditableUser extends AbstractAuditable { private String firstname; - @ManyToMany(cascade = { CascadeType.PERSIST, CascadeType.MERGE }) private Set roles = new HashSet(); + @ManyToMany( + cascade = { CascadeType.PERSIST, CascadeType.MERGE }) private final Set roles = new HashSet<>(); public AuditableUser() { this(null); } - public AuditableUser(Integer id) { + public AuditableUser(@Nullable Integer id) { this(id, null); } - public AuditableUser(Integer id, String firstname) { + public AuditableUser(@Nullable Integer id, String firstname) { setId(id); this.firstname = firstname; } /** * Returns the firstname. - * + * * @return the firstname */ public String getFirstname() { @@ -68,7 +69,7 @@ public class AuditableUser extends AbstractAuditable { /** * Sets the firstname. - * + * * @param firstname the firstname to set */ public void setFirstname(final String firstname) { diff --git a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java b/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java index b5f19e01a..ea07a4689 100644 --- a/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java +++ b/src/test/java/org/springframework/data/jpa/domain/support/AuditingEntityListenerTests.java @@ -18,7 +18,6 @@ package org.springframework.data.jpa.domain.support; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; -import java.time.Instant; import java.time.LocalDateTime; import java.util.Optional; @@ -40,7 +39,7 @@ import org.springframework.transaction.annotation.Transactional; /** * Integration test for {@link AuditingEntityListener}. - * + * * @author Oliver Gierke */ @RunWith(SpringJUnit4ClassRunner.class) diff --git a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java index d86e36b87..30792c8f8 100644 --- a/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/EclipseLinkNamespaceUserRepositoryTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2008-2014 the original author or authors. + * Copyright 2008-2017 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -43,14 +43,6 @@ public class EclipseLinkNamespaceUserRepositoryTests extends NamespaceUserReposi } - /** - * Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved. - */ - @Override - public void allowsExecutingPageableMethodWithNullPageable() { - - } - /** * Ignored until https://bugs.eclipse.org/bugs/show_bug.cgi?id=349477 is resolved. */ diff --git a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java index feffde7e5..6a356f94d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/UserRepositoryTests.java @@ -161,11 +161,6 @@ public class UserRepositoryTests { secondUser, thirdUser); } - @Test - public void savingNullCollectionIsNoOp() throws Exception { - assertThat(repository.saveAll(null)).isEmpty(); - } - @Test public void savingEmptyCollectionIsNoOp() throws Exception { assertThat(repository.saveAll(new ArrayList<>())).isEmpty(); @@ -536,7 +531,7 @@ public class UserRepositoryTests { public void returnsSameListIfNoSortIsGiven() throws Exception { flushTestUsers(); - assertSameElements(repository.findAll((Sort) null), repository.findAll()); + assertSameElements(repository.findAll(Sort.unsorted()), repository.findAll()); } @Test @@ -552,7 +547,7 @@ public class UserRepositoryTests { public void returnsAllAsPageIfNoPageableIsGiven() throws Exception { flushTestUsers(); - assertThat(repository.findAll((Pageable) null)).isEqualTo(new PageImpl(repository.findAll())); + assertThat(repository.findAll(Pageable.unpaged())).isEqualTo(new PageImpl<>(repository.findAll())); } @Test @@ -569,7 +564,7 @@ public class UserRepositoryTests { @Test public void executesPagedSpecificationsCorrectly() throws Exception { - Page result = executeSpecWithSort(null); + Page result = executeSpecWithSort(Sort.unsorted()); assertThat(result.getContent()).isSubsetOf(firstUser, thirdUser); } @@ -758,17 +753,17 @@ public class UserRepositoryTests { } @Test // DATAJPA-201 - public void allowsExecutingPageableMethodWithNullPageable() { + public void allowsExecutingPageableMethodWithUnpagedArgument() { flushTestUsers(); assertThat(repository.findByFirstname("Oliver", null)).containsOnly(firstUser); - Page page = repository.findByFirstnameIn(null, "Oliver"); + Page page = repository.findByFirstnameIn(Pageable.unpaged(), "Oliver"); assertThat(page.getNumberOfElements()).isEqualTo(1); assertThat(page.getContent()).contains(firstUser); - page = repository.findAll((Pageable) null); + page = repository.findAll(Pageable.unpaged()); assertThat(page.getNumberOfElements()).isEqualTo(4); assertThat(page.getContent()).contains(firstUser, secondUser, thirdUser, fourthUser); } @@ -868,6 +863,7 @@ public class UserRepositoryTests { flushTestUsers(); Page page = repository.findAll(new Specification() { + @Override public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) { return cb.equal(root.get("lastname"), "Gierke"); } @@ -941,11 +937,10 @@ public class UserRepositoryTests { assertThat(repository.existsByLastname("Hans Peter")).isEqualTo(false); } - @Test // DATAJPA-332 + @Test // DATAJPA-332, DATAJPA-1168 public void findAllReturnsEmptyIterableIfNoIdsGiven() { assertThat(repository.findAllById(Collections. emptySet())).isEmpty(); - assertThat(repository.findAllById((Iterable) null)).isEmpty(); } @Test // DATAJPA-391 @@ -1171,7 +1166,7 @@ public class UserRepositoryTests { flushTestUsers(); - List result = repository.findByAttributesIn(new HashSet(Arrays.asList("cool", "hip"))); + List result = repository.findByAttributesIn(new HashSet<>(Arrays.asList("cool", "hip"))); assertThat(result).containsOnly(firstUser, secondUser); } @@ -1230,7 +1225,7 @@ public class UserRepositoryTests { flushTestUsers(); - byte[] result = null; // repository.findBinaryDataByIdJpaQl(firstUser.getId()); + byte[] result = repository.findBinaryDataByIdNative(firstUser.getId()); assertThat(result.length).isEqualTo(data.length); assertThat(result).isEqualTo(data); @@ -1640,7 +1635,7 @@ public class UserRepositoryTests { Stream stream = repository.findAllByCustomQueryAndStream(); - final List users = new ArrayList(); + final List users = new ArrayList<>(); try { @@ -1666,7 +1661,7 @@ public class UserRepositoryTests { Stream stream = repository.readAllByFirstnameNotNull(); - final List users = new ArrayList(); + final List users = new ArrayList<>(); try { @@ -1692,7 +1687,7 @@ public class UserRepositoryTests { Stream stream = repository.streamAllPaged(PageRequest.of(0, 2)); - final List users = new ArrayList(); + final List users = new ArrayList<>(); try { diff --git a/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java b/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java index e8c37d4ac..62e8b35e3 100644 --- a/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/config/CustomRepositoryFactoryConfigTests.java @@ -26,7 +26,6 @@ import org.springframework.data.jpa.repository.custom.UserCustomExtendedReposito import org.springframework.data.jpa.repository.support.TransactionalRepositoryTests.DelegatingTransactionManager; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; -import org.springframework.util.Assert; /** * Annotation to exclude repository interfaces from being picked up and thus in consequence getting an instance being @@ -36,7 +35,7 @@ import org.springframework.util.Assert; * custom repository base class to implement methods declared in that intermediate interface. In this case you typically * derive your concrete repository interfaces from the intermediate one but don't want to create a Spring bean for the * intermediate interface. - * + * * @author Oliver Gierke * @author Mark Paluch */ @@ -44,11 +43,9 @@ import org.springframework.util.Assert; @ContextConfiguration(locations = "classpath:config/namespace-customfactory-context.xml") public class CustomRepositoryFactoryConfigTests { - @Autowired(required = false) - UserCustomExtendedRepository userRepository; + @Autowired(required = false) UserCustomExtendedRepository userRepository; - @Autowired - DelegatingTransactionManager transactionManager; + @Autowired DelegatingTransactionManager transactionManager; @Before public void setup() { diff --git a/src/test/java/org/springframework/data/jpa/repository/custom/package-info.java b/src/test/java/org/springframework/data/jpa/repository/custom/package-info.java new file mode 100644 index 000000000..054f40763 --- /dev/null +++ b/src/test/java/org/springframework/data/jpa/repository/custom/package-info.java @@ -0,0 +1,4 @@ +@NonNullApi +package org.springframework.data.jpa.repository.custom; + +import org.springframework.lang.NonNullApi; diff --git a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java b/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java index 6a2e43066..500dd985d 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/Jpa21UtilsTests.java @@ -35,6 +35,7 @@ import org.springframework.transaction.annotation.Transactional; /** * @author Christoph Strobl + * @author Mark Paluch */ @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration("classpath:application-context.xml") @@ -91,7 +92,7 @@ public class Jpa21UtilsTests { assertThat(colleagues, terminatesGraphWith("roles")); assertThat(colleagues, hasSubgraphs("colleagues")); - AttributeNode colleaguesOfColleagues = findNode("colleagues", colleagues); + AttributeNode colleaguesOfColleagues = findNode("colleagues", colleagues); assertThat(colleaguesOfColleagues, terminatesGraphWith("roles")); } @@ -111,7 +112,7 @@ public class Jpa21UtilsTests { assertThat(colleagues, terminatesGraphWith("roles")); assertThat(colleagues, hasSubgraphs("colleagues")); - AttributeNode colleaguesOfColleagues = findNode("colleagues", colleagues); + AttributeNode colleaguesOfColleagues = findNode("colleagues", colleagues); assertThat(colleaguesOfColleagues, terminatesGraphWith("roles")); } @@ -131,7 +132,7 @@ public class Jpa21UtilsTests { assertThat(colleagues, terminatesGraphWith("roles")); assertThat(colleagues, hasSubgraphs("colleagues")); - AttributeNode colleaguesOfColleagues = findNode("colleagues", colleagues); + AttributeNode colleaguesOfColleagues = findNode("colleagues", colleagues); assertThat(colleaguesOfColleagues, terminatesGraphWith("roles")); } diff --git a/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java index d4fe165bd..bd4cb711f 100644 --- a/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/query/StringQueryUnitTests.java @@ -26,12 +26,11 @@ import org.junit.rules.ExpectedException; import org.springframework.data.jpa.repository.query.StringQuery.InParameterBinding; import org.springframework.data.jpa.repository.query.StringQuery.LikeParameterBinding; import org.springframework.data.jpa.repository.query.StringQuery.ParameterBinding; -import org.springframework.data.repository.query.Param; import org.springframework.data.repository.query.parser.Part.Type; /** * Unit tests for {@link StringQuery}. - * + * * @author Oliver Gierke * @author Thomas Darimont */ @@ -281,7 +280,8 @@ public class StringQueryUnitTests { } /** - * @see JPA 2.1 specification, section 4.8 + * @see JPA 2.1 + * specification, section 4.8 */ @Test // DATAJPA-886 public void detectsConstructorExpressionForDefaultConstructor() { diff --git a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java index 87f34f1d2..71181c65b 100644 --- a/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java +++ b/src/test/java/org/springframework/data/jpa/repository/support/JpaRepositoryFactoryBeanUnitTests.java @@ -22,7 +22,6 @@ import static org.mockito.Mockito.*; import java.io.Serializable; import java.util.HashMap; import java.util.Map; -import java.util.Optional; import javax.persistence.EntityManager; import javax.persistence.metamodel.Metamodel; diff --git a/src/test/java/org/springframework/data/jpa/util/IsAttributeNode.java b/src/test/java/org/springframework/data/jpa/util/IsAttributeNode.java index 63e76fa62..a9f3baedc 100644 --- a/src/test/java/org/springframework/data/jpa/util/IsAttributeNode.java +++ b/src/test/java/org/springframework/data/jpa/util/IsAttributeNode.java @@ -25,18 +25,20 @@ import javax.persistence.Subgraph; import org.hamcrest.Description; import org.hamcrest.TypeSafeMatcher; +import org.springframework.lang.Nullable; import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; /** * @author Christoph Strobl + * @author Mark Paluch */ public class IsAttributeNode extends TypeSafeMatcher> { private boolean terminatingNodeCheck = false; private List nodes; private List subgraphs; - private List errors = new ArrayList(); + private final List errors = new ArrayList<>(); @Override protected boolean matchesSafely(AttributeNode item) { @@ -129,7 +131,7 @@ public class IsAttributeNode extends TypeSafeMatcher> { * @param graph * @return */ - public static AttributeNode findNode(String nodeName, EntityGraph graph) { + public static AttributeNode findNode(String nodeName, @Nullable EntityGraph graph) { if (graph == null) { return null; @@ -145,6 +147,7 @@ public class IsAttributeNode extends TypeSafeMatcher> { * @param nodes * @return */ + @Nullable public static AttributeNode findNode(String nodeName, List> nodes) { if (CollectionUtils.isEmpty(nodes)) { @@ -168,6 +171,7 @@ public class IsAttributeNode extends TypeSafeMatcher> { * @param node * @return */ + @Nullable public static AttributeNode findNode(String attributeName, AttributeNode node) { if (CollectionUtils.isEmpty(node.getSubgraphs())) { @@ -180,7 +184,7 @@ public class IsAttributeNode extends TypeSafeMatcher> { private List extractExistingAttributeNames(Subgraph graph) { - List result = new ArrayList(graph.getAttributeNodes().size()); + List result = new ArrayList<>(graph.getAttributeNodes().size()); for (AttributeNode node : graph.getAttributeNodes()) { result.add(node.getAttributeName()); } @@ -193,9 +197,9 @@ public class IsAttributeNode extends TypeSafeMatcher> { * @param nodeNames * @return */ - public static IsAttributeNode terminatesGraphWith(String... nodeNames) { + public static IsAttributeNode terminatesGraphWith(String... nodeNames) { - IsAttributeNode matcher = new IsAttributeNode(); + IsAttributeNode matcher = new IsAttributeNode<>(); matcher.nodes = Arrays.asList(nodeNames); return matcher; } @@ -206,9 +210,9 @@ public class IsAttributeNode extends TypeSafeMatcher> { * * @return */ - public static IsAttributeNode hasSubgraphs(String... subgraphNames) { + public static IsAttributeNode hasSubgraphs(String... subgraphNames) { - IsAttributeNode matcher = new IsAttributeNode(); + IsAttributeNode matcher = new IsAttributeNode<>(); matcher.subgraphs = Arrays.asList(subgraphNames); return matcher; } @@ -219,9 +223,9 @@ public class IsAttributeNode extends TypeSafeMatcher> { * * @return */ - public static IsAttributeNode terminatesGraph() { + public static IsAttributeNode terminatesGraph() { - IsAttributeNode matcher = new IsAttributeNode(); + IsAttributeNode matcher = new IsAttributeNode<>(); matcher.terminatingNodeCheck = true; return matcher; }