Skip to content

DATACMNS-966 - Add annotation that specify allowed sort properties. #190

New issue

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

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

Already on GitHub? Sign in to your account

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
1d3af0c
DATACMNS-937 - Support for JavaSlang's Option type as nullable wrapper.
odrotbohm Nov 19, 2016
cca9cd8
DATACMNS-937 - Polishing.
odrotbohm Nov 19, 2016
3cd76d2
DATACMNS-937 - Document JavaSlang's Option type as query method retur…
odrotbohm Nov 14, 2016
cde3404
DATACMNS-939 - Do not create queries for static interface methods.
mp911de Nov 21, 2016
34002d8
DATACMNS-937 - Fixed spelling of Javaslang related code.
odrotbohm Nov 21, 2016
50f3565
DATACMNS-933 - Updated changelog.
odrotbohm Nov 23, 2016
0cc17ff
DATACMNS-943 - We prefer direct matches on repository query method ov…
odrotbohm Nov 24, 2016
72e349b
DATACMNS-918 - Provide interfaces for Pageable and Sort method argume…
mp911de Sep 27, 2016
d3c70d8
DATACMNS-918 - Polishing.
odrotbohm Nov 29, 2016
0fc9770
DATACMNS-928 - Support for publishing events from aggregate roots.
odrotbohm Oct 21, 2016
bdf4738
DATACMNS-928 - Polishing.
odrotbohm Nov 22, 2016
c6f9c40
DATACMNS-928 - Refinements for aggregate root domain event publication.
odrotbohm Nov 30, 2016
8062698
DATACMNS-928 - Reference documentation for event publication support.
odrotbohm Nov 30, 2016
3125bbf
DATACMNS-951 - Added Converter implementations for JSR-310 Duration a…
Nov 23, 2016
8d67c84
DATACMNS-951 - Polishing.
odrotbohm Dec 4, 2016
76d1995
DATACMNS-952 - Switched from component scanning to SpringFactoriesLoa…
odrotbohm Dec 5, 2016
2377cba
DATACMNS-934 - BasicPersistentEntity.addAssociation(…) drops null val…
odrotbohm Dec 5, 2016
e1d1a68
DATACMNS-953 - Fixed application of GenericPropertyMatchers.exact().
odrotbohm Dec 6, 2016
8af061f
DATACMNS-941 - Support for downcasts in Querydsl web bindings.
odrotbohm Dec 12, 2016
8f748d6
DATACMNS-956 - Ensure consistent use of ClassTypeInformation.from(…).
odrotbohm Dec 12, 2016
33b65fb
DATACMNS-940 - Support for Javaslang collections as repository return…
odrotbohm Dec 12, 2016
902cb44
DATACMNS-940 - Fix query method query result type detection for custo…
odrotbohm Dec 13, 2016
a76aa4d
DATACMNS-958 - Use ExposeInvocationInterceptor.ADVISOR in RepositoryF…
odrotbohm Dec 14, 2016
3a24bee
DATACMNS-959 - RepositoryFactorySupport now registers interceptor to …
odrotbohm Dec 14, 2016
259a1e0
DATACMNS-891 - Moved to constructor injection for RepositoryFactoryBe…
odrotbohm Dec 14, 2016
aa993bb
DATACMNS-960 - RevisionRepository now extends Repository.
odrotbohm Dec 15, 2016
4d53788
DATACMNS-961 - Use newer Java 8 on Travis CI.
mp911de Dec 16, 2016
f4e9c2c
DATACMNS-962 - Fixed typo in reference documentation.
odrotbohm Dec 20, 2016
c8fb117
DATACMNS-963 - Returned type returns distinct input properties now.
odrotbohm Dec 21, 2016
b923331
DATACMNS-889 - Updated changelog.
odrotbohm Dec 21, 2016
cba32b3
DATACMNS-889 - Prepare 1.13 RC1 (Ingalls).
odrotbohm Dec 21, 2016
6338778
DATACMNS-889 - Release version 1.13 RC1 (Ingalls).
odrotbohm Dec 21, 2016
621e393
DATACMNS-889 - Prepare next development iteration.
odrotbohm Dec 21, 2016
1eff8b1
DATACMNS-889 - After release cleanups.
odrotbohm Dec 21, 2016
07430b9
DATACMNS-932 - Updated changelog.
odrotbohm Dec 21, 2016
c290095
DATACMNS-966 - Add annotation that specify allowed sort properties.
kazuki43zoo Dec 23, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,19 +1,31 @@
language: java

jdk:
- oraclejdk8

env:
matrix:
- PROFILE=ci
- PROFILE=spring43-next
- PROFILE=spring5
- PROFILE=spring5-next
- PROFILE=querydsl-next

addons:
apt:
packages:
- oracle-java8-installer

cache:
directories:
- $HOME/.m2

sudo: false

install: true

script: "mvn clean dependency:list test -P${PROFILE} -Dsort"

notifications:
slack:
secure: PWyr3+7uTnHzZSrJY2DrowwdYODlIeFZB6tzuq+i/vYqMlX2GGXGQN9YmqbKPeaVZBdSI7YrI1UDa5cPAAXoTjn/JewkL0RbdTeIS6PGCpcpAb5rFMYtOhEDBrFB/SRiMH4tw86bRNKq/SrUWmpkzDtTzLrw7JceumgyqnrT6GY=
8 changes: 8 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<properties>
<dist.key>DATACMNS</dist.key>
<javaslang>2.0.4</javaslang>
<scala>2.11.7</scala>
<xmlbeam>1.4.8</xmlbeam>
</properties>
Expand Down Expand Up @@ -143,6 +144,13 @@
<optional>true</optional>
</dependency>

<dependency>
<groupId>io.javaslang</groupId>
<artifactId>javaslang</artifactId>
<version>${javaslang}</version>
<optional>true</optional>
</dependency>

<dependency>
<groupId>javax.el</groupId>
<artifactId>el-api</artifactId>
Expand Down
36 changes: 33 additions & 3 deletions src/main/asciidoc/repositories.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ NOTE: Note, that the intermediate repository interface is annotated with `@NoRep
Using a unique Spring Data module in your application makes things simple hence, all repository interfaces in the defined scope are bound to the Spring Data module. Sometimes applications require using more than one Spring Data module. In such case, it's required for a repository definition to distinguish between persistence technologies. Spring Data enters strict repository configuration mode because it detects multiple repository factories on the class path. Strict configuration requires details on the repository or the domain class to decide about Spring Data module binding for a repository definition:

1. If the repository definition <<repositories.multiple-modules.types,extends the module-specific repository>>, then it's a valid candidate for the particular Spring Data module.
2. If the domain class is <<repositories.multiple-modules.annotations,annotated with the module-specific type annotation>>, then it's a valid candidate for the particular Spring Data module. Spring Data modules accept either 3rd party annotations (such as JPA's `@Entity`) or provide own annotations such as `@Document` for Spring Data MongoDB/Spring Data Elasticsearch.
2. If the domain class is <<repositories.multiple-modules.annotations,annotated with the module-specific type annotation>>, then it's a valid candidate for the particular Spring Data module. Spring Data modules accept either 3rd party annotations (such as JPA's `@Entity`) or provide own annotations such as `@Document` for Spring Data MongoDB/Spring Data Elasticsearch.

[[repositories.multiple-modules.types]]
.Repository definitions using Module-specific Interfaces
Expand Down Expand Up @@ -306,7 +306,7 @@ public class Person {
This example shows a domain class using both JPA and Spring Data MongoDB annotations. It defines two repositories, `JpaPersonRepository` and `MongoDBPersonRepository`. One is intended for JPA and the other for MongoDB usage. Spring Data is no longer able to tell the repositories apart which leads to undefined behavior.
====

<<repositories.multiple-modules.types,Repository type details>> and <<repositories.multiple-modules.annotations,identifying domain class annotations>> are used for strict repository configuration identify repository candidates for a particular Spring Data module. Using multiple persistence technology-specific annotations on the same domain type is possible to reuse domain types across multiple persistence technologies, but then Spring Data is no longer able to determine a unique module to bind the repository.
<<repositories.multiple-modules.types,Repository type details>> and <<repositories.multiple-modules.annotations,identifying domain class annotations>> are used for strict repository configuration identify repository candidates for a particular Spring Data module. Using multiple persistence technology-specific annotations on the same domain type is possible to reuse domain types across multiple persistence technologies, but then Spring Data is no longer able to determine a unique module to bind the repository.

The last way to distinguish repositories is scoping repository base packages. Base packages define the starting points for scanning for repository interface definitions which implies to have repository definitions located in the appropriate packages. By default, annotation-driven configuration uses the package of the configuration class. The <<repositories.create-instances.spring,base package in XML-based configuration>> is mandatory.

Expand Down Expand Up @@ -414,7 +414,7 @@ List<User> findByLastname(String lastname, Pageable pageable);
----
====

The first method allows you to pass an `org.springframework.data.domain.Pageable` instance to the query method to dynamically add paging to your statically defined query. A `Page` knows about the total number of elements and pages available. It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive depending on the store used, `Slice` can be used as return instead. A `Slice` only knows about whether there's a next `Slice` available which might be just sufficient when walking thought a larger result set.
The first method allows you to pass an `org.springframework.data.domain.Pageable` instance to the query method to dynamically add paging to your statically defined query. A `Page` knows about the total number of elements and pages available. It does so by the infrastructure triggering a count query to calculate the overall number. As this might be expensive depending on the store used, `Slice` can be used as return instead. A `Slice` only knows about whether there's a next `Slice` available which might be just sufficient when walking through a larger result set.

Sorting options are handled through the `Pageable` instance too. If you only need sorting, simply add an `org.springframework.data.domain.Sort` parameter to your method. As you also can see, simply returning a `List` is possible as well. In this case the additional metadata required to build the actual `Page` instance will not be created (which in turn means that the additional count query that would have been necessary not being issued) but rather simply restricts the query to look up only the given range of entities.

Expand Down Expand Up @@ -736,6 +736,36 @@ A corresponding attribute is available in the XML namespace.
----
====

[[core.domain-events]]
== Publishing events from aggregate roots

Entities managed by repositories are aggregate roots.
In a Domain-Driven Design application, these aggregate roots usually publish domain events.
Spring Data provides an annotation `@DomainEvents` you can use on a method of your aggregate root to make that publication as easy as possible.

.Exposing domain events from an aggregate root
====
[source, java]
----
class AnAggreagteRoot {

@DomainEvents <1>
Collection<Object> domainEvents() {
// … return events you want to get published here
}

@AfterDomainEventsPublication <2>
void callbackMethod() {
// … potentially clean up domain events list
}
}
----
<1> The method using `@DomainEvents` can either return a single event instance or a collection of events. It must not take any arguments.
<2> After all events have been published, a method annotated with `@AfterDomainEventsPublication`. It e.g. can be used to potentially clean the list of events to be published.
====

The methods will be called every time one of a Spring Data repository's `save(…)` methods is called.

[[core.extensions]]
== Spring Data extensions

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ NOTE: Geospatial types like (`GeoResult`, `GeoResults`, `GeoPage`) are only avai
|`Collection<T>`|A `Collection`.
|`List<T>`|A `List`.
|`Optional<T>`|A Java 8 or Guava `Optional`. Expects the query method to return one result at most. In case no result is found `Optional.empty()`/`Optional.absent()` is returned. More than one result will trigger an `IncorrectResultSizeDataAccessException`.
|`Option<T>`|An either Scala or JavaSlang `Option` type. Semantically same behavior as Java 8's `Optional` described above.
|`Stream<T>`|A Java 8 `Stream`.
|`Future<T>`|A `Future`. Expects method to be annotated with `@Async` and requires Spring's asynchronous method execution capability enabled.
|`CompletableFuture<T>`|A Java 8 `CompletableFuture`. Expects method to be annotated with `@Async` and requires Spring's asynchronous method execution capability enabled.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2013-2015 the original author or authors.
* Copyright 2013-2016 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.
Expand All @@ -19,10 +19,12 @@
import static java.time.LocalDateTime.*;
import static java.time.ZoneId.*;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.Period;
import java.time.ZoneId;
import java.util.ArrayList;
import java.util.Arrays;
Expand All @@ -38,6 +40,7 @@
* Helper class to register JSR-310 specific {@link Converter} implementations in case the we're running on Java 8.
*
* @author Oliver Gierke
* @author Barak Schoster
*/
public abstract class Jsr310Converters {

Expand Down Expand Up @@ -66,6 +69,10 @@ public abstract class Jsr310Converters {
converters.add(InstantToDateConverter.INSTANCE);
converters.add(ZoneIdToStringConverter.INSTANCE);
converters.add(StringToZoneIdConverter.INSTANCE);
converters.add(DurationToStringConverter.INSTANCE);
converters.add(StringToDurationConverter.INSTANCE);
converters.add(PeriodToStringConverter.INSTANCE);
converters.add(StringToPeriodConverter.INSTANCE);

return converters;
}
Expand Down Expand Up @@ -181,4 +188,48 @@ public ZoneId convert(String source) {
return ZoneId.of(source);
}
}

@WritingConverter
public static enum DurationToStringConverter implements Converter<Duration, String> {

INSTANCE;

@Override
public String convert(Duration duration) {
return duration.toString();
}
}

@ReadingConverter
public static enum StringToDurationConverter implements Converter<String, Duration> {

INSTANCE;

@Override
public Duration convert(String s) {
return Duration.parse(s);
}
}

@WritingConverter
public static enum PeriodToStringConverter implements Converter<Period, String> {

INSTANCE;

@Override
public String convert(Period period) {
return period.toString();
}
}

@ReadingConverter
public static enum StringToPeriodConverter implements Converter<String, Period> {

INSTANCE;

@Override
public Period convert(String s) {
return Period.parse(s);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright 2016 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.domain;

import lombok.Getter;

import java.util.ArrayList;
import java.util.List;

import org.springframework.util.Assert;

/**
* Convenience base class for aggregate roots that exposes a {@link #registerEvent(Object)} to capture domain events and
* expose them via {@link #getDomainEvents()}. The implementation is using the general event publication mechanism
* implied by {@link DomainEvents} and {@link AfterDomainEventPublication}. If in doubt or need to customize anything
* here, rather build your own base class and use the annotations directly.
*
* @author Oliver Gierke
* @since 1.13
*/
public class AbstractAggregateRoot {

/**
* All domain events currently captured by the aggregate.
*/
@Getter(onMethod = @__(@DomainEvents)) //
private transient final List<Object> domainEvents = new ArrayList<Object>();

/**
* Registers the given event object for publication on a call to a Spring Data repository's save method.
*
* @param event must not be {@literal null}.
* @return
*/
protected <T> T registerEvent(T event) {

Assert.notNull(event, "Domain event must not be null!");

this.domainEvents.add(event);
return event;
}

/**
* Clears all domain events currently held. Usually invoked by the infrastructure in place in Spring Data
* repositories.
*/
@AfterDomainEventPublication
public void clearDomainEvents() {
this.domainEvents.clear();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*
* Copyright 2016 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.domain;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Annotation to be used on a method of a Spring Data managed aggregate to get invoked after the events of an aggregate
* have been published.
*
* @author Oliver Gierke
* @see DomainEvents
* @since 1.13
* @soundtrack Benny Greb - September (Moving Parts Live)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface AfterDomainEventPublication {

}
38 changes: 38 additions & 0 deletions src/main/java/org/springframework/data/domain/DomainEvents.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright 2016 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.domain;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import org.springframework.context.ApplicationEventPublisher;

/**
* {@link DomainEvents} can be used on methods of aggregate roots managed by Spring Data repositories to publish the
* events returned by that method as Spring application events.
*
* @author Oliver Gierke
* @see ApplicationEventPublisher
* @see AfterDomainEventPublication
* @since 1.13
* @soundtrack Benny Greb - Soulfood (Moving Parts Live)
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.METHOD, ElementType.ANNOTATION_TYPE })
public @interface DomainEvents {
}
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,7 @@ public static GenericPropertyMatcher startsWith() {
* @return
*/
public static GenericPropertyMatcher exact() {
return new GenericPropertyMatcher().startsWith();
return new GenericPropertyMatcher().exact();
}

/**
Expand Down
Loading