Spring data gremlin makes it easier to implement Graph based repositories. This module extends Spring Data to allow support for potentially any Graph database that implements the Tinkerpop Blueprints 2.x API.
- All the great features of Spring Data
- Support for OrientDB and TitanDB out of the box
- Schema creation in supported databases
- Support to build repositories based on Spring using our custom set of annotations, spring-data-neo4j or JPA annotations.
- Vertex and Edge repository support
- Pagination support
- Unique, non-unique and spatial indices supported
- Support for Gremlin query language through the
@Queryannotation - Support for native queries (Eg. OrientDB SQL) through the
@Queryannotation - JavaConfig based repository configuration by introducing @EnableGremlinRepositories
MapandCompositeResultquery result objects- ORM support for java.io.Serializable and arbitrary classes as JSON
Below is a list of default annotations used by the DefaultSchemaGenerator.
@Vertexmaps anObjectto aVertex@Edgemaps anObjectto anEdge@Embeddablemaps anObjectto set of properties to be embedded in a "parent" vertex@Idmaps an instance variable to the vertex or edge ID@Indexused for indexing properties including unique, spatial and non-unique@Propertymaps an instance variable to a vertex property (optional, only required if you want to name it differently, or serialize it as JSON)@Embedembeds the referencedObjectin the "parent" vertex@PropertyOverridecan be used within@Embedfor overriding properties within@Embeddables.@Ignoreignores a variable@Enumeratedallows for mapping an enum as an ordinal, otherwise String is the default mapping@Linkcreates a link from this vertex to the referencedObject's vertex orCollection's verticies using the name of the field as default or the optionaltypeparameter as the link label@LinkViacreates anEdgebased the the referencedObjectorCollectionwhich must be a@Edge@FromVertexdefines the starting (or OUT) vertex of a@Edge@ToVertexdefines the ending (or IN) vertex of a@Edge
Below is a list of supported annotations used by the Neo4jSchemaGenerator. These annotations are part of the spring-data-neo4j platform.
@NodeEntitymaps anObjectto aVertex@RelationshipEntitymaps anObjectto anEdge@GraphIdmaps an instance variable to the vertex or edge ID@Indexedused for indexing properties@GraphPropertymaps an instance variable to a vertex property (optional, only required if you want to name it differently)@RelatedTocreates a link from this vertex to the referencedObject's vertex orCollection's verticies using the name of the field as default or the optionaltypeparameter as the link label@RelatedToViacreates anEdgebased the the referencedObjectorCollectionwhich must be a@RelationshipEntity@StartNodedefines the starting (or OUT) vertex of a@RelationshipEntity@EndNodedefines the ending (or IN) vertex of a@RelationshipEntity
Below is a list of supported annotations used by the JpaSchemaGenerator:
@Entitymaps anObjectto aVertex@Embeddablemaps anObjectto set of properties to be embedded in a "parent" vertex@Idmaps an instance variable to the vertex ID@Columnmaps an instance variable to a vertex property@Embeddedembeds the referencedObjectin the "parent" vertex@AttributeOverridesand@AttributeOverridecan be used for overriding@Embeddedproperty names.@OneToOnecreates an outgoing link from this vertex to the referencedObject's vertex using the name of the field as default or the optional@Column's name field as the link label@OneToManycreates an outgoing link from this vertex to all of the referencedCollection's vertices using the name of the field as default or the optional@Column's name field as the link label@Transientmarks an instance variable as transient@Enumeratedallows for mapping an enum as a ordinal, otherwise String is the default mapping
Currenlty only SNAPSHOT builds are being uploaded to sonatype so you will need to add https://oss.sonatype.org/content/repositories/snapshots/ repository URL to your build configuration.
<repositories>
<repository>
<id>spring.data.gremlin.snapshot</id>
<name>Spring Data Gremlin SNAPHSHOT</name>
<url>https://oss.sonatype.org/content/repositories/snapshots</url>
</repository>
</repositories>
repositories {
//...
maven { url 'https://oss.sonatype.org/content/repositories/snapshots' }
}
Once you have your build configuration setup you need to add the correct dependencies. To do that you need to decide which database and schema generator you want to use. If you are starting from scratch, then the default schema generator is for you.
OrientDB - com.github.gjrwebber:spring-data-gremlin-orientdb:0.1.0-SNAPSHOT
TitanDB - com.github.gjrwebber:spring-data-gremlin-titan:0.1.0-SNAPSHOT
Default - No further dependency
JPA - com.github.gjrwebber:spring-data-gremlin-schemagen-jpa:0.1.0-SNAPSHOT
Neo4j - com.github.gjrwebber:spring-data-gremlin-schemagen-neo4j:0.1.0-SNAPSHOT
Using OrientDB database with Neo4j schema generator:
<dependency>
<groupId>com.github.gjrwebber</groupId>
<artifactId>spring-data-gremlin-orientdb</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.github.gjrwebber</groupId>
<artifactId>spring-data-gremlin-schemagen-neo4j</artifactId>
<version>0.1.0-SNAPSHOT</version>
</dependency>
Using TitanDB with default schema generator:
compile("com.github.gjrwebber:spring-data-gremlin-titan:0.1.0-SNAPSHOT")
Create your domain objects. I have used the default generator for mapping the schema, but you can also use spring-data-neo4j or JPA annotations if you wish.
Note: Have a look at the test subproject for more examples.
@Vertex
public class Person {
@Id
private String id;
@Property("customer_name")
private String name;
// No need to annotate simple types
private boolean active;
@Link("lives_at")
private Address address;
@LinkVia
private Set<Located> locations;
@LinkVia
private Located currentLocation;
// Annotation optional
private Set<House> owned;
@Property(type = JSON) // Will serialize as JSON even though House is a java.io.Serializable
private House owns;
@Property(type = JSON)
private Set<Pet> pets;
// Annotation optional
private Pet favouritePet;
}
@Vertex
public class Address {
@Id
private String id;
@Embed(propertyOverrides = { @PropertyOverride(name = "name", property = @Property("countryName")) })
private Country country;
private String city;
private String street;
}
@Embeddable
public class Country {
private String name;
}
@Vertex
public class Location {
@Id
private String id;
private Date date;
@Index(type = SPATIAL_LATITUDE)
private double latitude;
@Index(type = SPATIAL_LONGITUDE)
private double longitude;
}
@Edge("was_located")
public class Located {
@Id
private String id;
@Property("location_date")
private Date date;
@FromVertex
private Person person;
@ToVertex
private Location location;
}
public class House implements Serializable {
private int rooms;
}
public class Pet {
public enum TYPE {
CAT,DOG,HORSE;
}
private String name;
private TYPE type;
}
Now create a repository for the Vertex Person:
public interface PersonRepository extends GremlinRepository<Person> {
List<Person> findByLastName(String lastName);
List<Person> findByLastNameLike(String lastName);
List<Person> findByFirstNameAndLastName(String firstName, String lastName);
List<Person> findByFirstNameOrLastName(String firstName, String lastName);
List<Person> findByFirstNameLike(String string);
@Query(value = "graph.V().has('firstName', ?)")
List<Person> findByFirstName(String firstName);
@Query(value = "graph.V().has('firstName', ?)")
Page<Person> findByFirstName(String firstName, Pageable pageable);
@Query(value = "graph.V().has('firstName', ?)")
List<Map<String, Object>> findMapByFirstName(String firstName);
@Query(value = "graph.V().has('firstName', ?)")
Map<String, Object> findSingleMapByFirstName(String firstName);
List<Person> findByAddress_City(String city);
@Query(value = "delete vertex from (select from Person where firstName <> ?)", nativeQuery = true, modify = true)
Integer deleteAllExceptPerson(String firstName);
@Query(value = "select expand(in('was_located_at')) from (select from Location where [latitude,longitude,$spatial] near [?,?,{\"maxDistance\":?}])", nativeQuery = true)
Page<Person> findNear(double latitude, double longitude, double radius, Pageable pageable);
}
And one for the Edge Located:
public interface LocatedRepository extends GremlinRepository<Located> {
@Query(value = "graph.V().has('firstName', ?).outE('Location')")
List<Located> findAllLocatedForUser(String name);
}
Wire it up:
@Configuration
@EnableTransactionManagement
@EnableGremlinRepositories(basePackages = "test.repos", repositoryFactoryBeanClass = GremlinRepositoryFactoryBean.class)
public class Configuration {
@Bean
public OrientDBGremlinGraphFactory orientDBGraphFactory() {
OrientDBGremlinGraphFactory factory = new OrientDBGremlinGraphFactory();
factory.setUrl("memory:spring-data-orientdb-db");
factory.setUsername("admin");
factory.setPassword("admin");
return factory;
}
@Bean
public GremlinTransactionManager transactionManager() {
return new GremlinTransactionManager(orientDBGraphFactory());
}
@Bean
public GremlinSchemaFactory schemaFactory() {
return new GremlinSchemaFactory();
}
@Bean
public SchemaGenerator schemaGenerator() {
return new DefaultSchemaGenerator(new OrientDbIdEncoder());
}
@Bean
public SchemaWriter schemaWriter() {
return new OrientDbSchemaWriter();
}
@Bean
public static GremlinBeanPostProcessor gremlinSchemaManager(SchemaGenerator schemaGenerator) {
return new GremlinBeanPostProcessor(schemaGenerator, "test.domain");
}
@Bean
public GremlinGraphAdapter graphAdapter() {
return new OrientDBGraphAdapter();
}
@Bean
public GremlinRepositoryContext databaseContext(GremlinGraphFactory graphFactory, GremlinGraphAdapter graphAdapter, GremlinSchemaFactory schemaFactory, SchemaWriter schemaWriter) {
return new GremlinRepositoryContext(graphFactory, graphAdapter, schemaFactory, schemaWriter, OrientDBGremlinRepository.class, NativeOrientdbGremlinQuery.class);
}
}
##TODO
- Spring auto configuration
Many to many relationshipsLinks as entitiesEdge repositoriesSerializable class mappingArbitrary class mapping- Lazy fetching
- Index for multiple properties
- Allow for IDs other than String
- Repository definitions using
Neo4j, Frames orsome other custom implementation. - More Blueprints implementations (Neo4j, ArangoDB, Blazegraph, etc.)
- Migrate to Tinkerpop 3.0
##Acknowledgement This project would not have been possible without the hard work done by the spring-data-orientdb team. A lot of code and concepts were reused and reshaped. Thanks.