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; }