#386 - Add integration test for deep nested derived queries support.

SDN supports derived queries over several, deeply nested properties in the following version combinations:
- Spring Data Kay SR9 + Neo4j-OGM 3.0.4
- Spring Data Lovelace RC1 + Neo4j-OGM 3.1.1

As of writing, Spring Boot 2.0.4 picks up Spring Data Kay SR9 + Neo4j-OGM 3.1.0. This is an unsupported combination, see https://github.com/spring-projects/spring-boot/issues/13999.

This tests asserts the correct versions for Spring Boot 2.1.0+ and 2.0.5+. Also fixes the use of final field relationships in the domain classes used in the test and removed the usage of deprecated API.
This commit is contained in:
Michael J. Simons
2018-08-06 16:54:14 +02:00
committed by Oliver Gierke
parent dc58547743
commit 802b5564c4
5 changed files with 102 additions and 27 deletions

View File

@@ -19,12 +19,12 @@ package example.springdata.neo4j;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import java.util.HashSet;
import java.util.Set;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
@@ -33,16 +33,20 @@ import org.neo4j.ogm.annotation.Relationship;
*
* @author Luanne Misquitta
* @author Oliver Gierke
* @author Michael J. Simons
*/
@NodeEntity(label = "Actor")
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@RequiredArgsConstructor
@Getter
public class Actor {
private @GraphId Long id;
private final String name;
private final @Relationship(type = "ACTED_IN") Set<Role> roles = new HashSet<>();
private @Id @GeneratedValue Long id;
private String name;
private @Relationship(type = "ACTED_IN") Set<Role> roles = new HashSet<>();
public Actor(String name) {
this.name = name;
}
public void actedIn(Movie movie, String roleName) {

View File

@@ -15,11 +15,22 @@
*/
package example.springdata.neo4j;
import java.util.List;
import org.springframework.data.neo4j.repository.Neo4jRepository;
/**
* {@link GraphRepository} for {@link Actor}s.
* {@link Neo4jRepository} for {@link Actor actors}.
*
* @author Luanne Misquitta
* @author Michael J. Simons
*/
public interface ActorRepository extends Neo4jRepository<Actor, Long> {}
public interface ActorRepository extends Neo4jRepository<Actor, Long> {
/**
* Nested property from select from roles -> movie -> title,
* where this here represents the start node in a relationship and movie the end node.
* @param title
* @return
*/
List<Actor> findAllByRolesMovieTitle(String title);
}

View File

@@ -19,12 +19,12 @@ package example.springdata.neo4j;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import java.util.HashSet;
import java.util.Set;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.NodeEntity;
import org.neo4j.ogm.annotation.Relationship;
@@ -33,14 +33,18 @@ import org.neo4j.ogm.annotation.Relationship;
*
* @author Luanne Misquitta
* @author Oliver Gierke
* @author Michael J. Simons
*/
@NodeEntity(label = "Movie")
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@RequiredArgsConstructor
@Getter
public class Movie {
private @GraphId Long id;
private final String title;
private final @Relationship(type = "ACTED_IN", direction = "INCOMING") Set<Role> roles = new HashSet<>();
private @Id @GeneratedValue Long id;
private String title;
private @Relationship(type = "ACTED_IN", direction = "INCOMING") Set<Role> roles = new HashSet<>();
public Movie(String title) {
this.title = title;
}
}

View File

@@ -18,10 +18,10 @@ package example.springdata.neo4j;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.RequiredArgsConstructor;
import org.neo4j.ogm.annotation.EndNode;
import org.neo4j.ogm.annotation.GraphId;
import org.neo4j.ogm.annotation.GeneratedValue;
import org.neo4j.ogm.annotation.Id;
import org.neo4j.ogm.annotation.RelationshipEntity;
import org.neo4j.ogm.annotation.StartNode;
@@ -29,15 +29,21 @@ import org.neo4j.ogm.annotation.StartNode;
* A Role relationship entity between an actor and movie.
*
* @author Luanne Misquitta
* @author Michael J. Simons
*/
@RelationshipEntity(type = "ACTED_IN")
@NoArgsConstructor(force = true, access = AccessLevel.PRIVATE)
@RequiredArgsConstructor
@Getter
public class Role {
private @GraphId Long id;
private final @StartNode Actor actor;
private final String role;
private final @EndNode Movie movie;
private @Id @GeneratedValue Long id;
private @StartNode Actor actor;
private String role;
private @EndNode Movie movie;
Role(Actor actor, String role, Movie movie) {
this.actor = actor;
this.role = role;
this.movie = movie;
}
}

View File

@@ -16,20 +16,27 @@
package example.springdata.neo4j;
import static org.assertj.core.api.Assertions.*;
import static org.junit.Assume.*;
import java.util.Optional;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringBootVersion;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.util.Version;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ClassUtils;
/**
* Simple integration test demonstrating the use of the ActorRepository
*
* @author Luanne Misquitta
* @author Oliver Gierke
* @author Michael J. Simons
*/
@RunWith(SpringJUnit4ClassRunner.class)
@SpringBootTest
@@ -37,14 +44,12 @@ import org.springframework.transaction.annotation.Transactional;
public class ActorRepositoryIntegrationTest {
@SpringBootApplication
static class ExampleConfig {}
static class ExampleConfig {
}
@Autowired ActorRepository actorRepository;
/**
* @see #131
*/
@Test
@Test // #131
public void shouldBeAbleToSaveAndLoadActor() {
Movie goblet = new Movie("Harry Potter and the Goblet of Fire");
@@ -57,7 +62,52 @@ public class ActorRepositoryIntegrationTest {
assertThat(actorRepository.findById(daniel.getId())).hasValueSatisfying(actor -> {
assertThat(actor.getName()).isEqualTo(daniel.getName());
assertThat(actor.getRoles()).hasSize(1).first()
.satisfies(role -> assertThat(role.getRole()).isEqualTo("Harry Potter"));
.satisfies(role -> assertThat(role.getRole()).isEqualTo("Harry Potter"));
});
}
@Test // #386
public void shouldBeAbleToHandleNestedProperties() {
assumeTrue(thatSupportForNestedPropertiesIsAvailable());
Movie theParentTrap = new Movie("The Parent Trap");
Movie iKnowWhoKilledMe = new Movie("I Know Who Killed Me");
Actor lindsayLohan = new Actor("Lindsay Lohan");
lindsayLohan.actedIn(theParentTrap, "Hallie Parker");
lindsayLohan.actedIn(theParentTrap, "Annie James");
lindsayLohan.actedIn(iKnowWhoKilledMe, "Aubrey Fleming");
lindsayLohan.actedIn(iKnowWhoKilledMe, "Dakota Moss");
actorRepository.save(lindsayLohan);
Actor nealMcDonough = new Actor("Neal McDonough");
nealMcDonough.actedIn(iKnowWhoKilledMe, "Daniel Fleming");
actorRepository.save(nealMcDonough);
assertThat(actorRepository.findAllByRolesMovieTitle(iKnowWhoKilledMe.getTitle())).hasSize(2)
.extracting(Actor::getName).contains(lindsayLohan.getName(), nealMcDonough.getName());
}
private static boolean thatSupportForNestedPropertiesIsAvailable() {
Version minVersion = Version.parse("2.0.5");
Optional<String> currentSpringBootVersion = Optional.ofNullable(SpringBootVersion.getVersion());
return currentSpringBootVersion.map(Version::parse)
.map(v -> v.isGreaterThanOrEqualTo(minVersion))
.orElseGet(ActorRepositoryIntegrationTest::fallBackToVersionSpecificClasses);
}
private static boolean fallBackToVersionSpecificClasses() {
ClassLoader usedClassLoader = ActorRepositoryIntegrationTest.class.getClassLoader();
String fqnBoot210Class = "org.springframework.boot.autoconfigure.insight.InsightsProperties";
String fqnBoot205Class = "org.springframework.boot.autoconfigure.security.servlet.RequestMatcherProvider";
return ClassUtils.isPresent(fqnBoot210Class, usedClassLoader) || ClassUtils
.isPresent(fqnBoot205Class, usedClassLoader);
}
}