GraphAwareTM
Building High Performance Applications with Spring Data Neo4j 4
Vince Bickers, GraphAware
graphaware.com
@graph_aware
Agenda● A bit about Neo4j and the Spring Data project
● A brief history of SDN
● SDN 4: Spring Data + Standalone Java OGM
● Conference Demo
o Data model
o Code
o Live demo!
● Roadmap
● Q & A
Neo4j is a Graph Database•Nodes & Edges (Relationships)
• Index-free adjacency means super-fast traversals
•Embedded Client and Remote Server
•Schema-flexible!
•Cypher Query Language
When worlds collide…The impedance mismatch
Spring Data
•Most active Spring Project
•Consistent APIs to access both RDBMS and NoSQL stores
• JPA, Neo4j, MongoDB, Redis, CouchBase…
•Convenient abstractions for Spring developers
Quick History of SD-Neo4j● SDN 0.x - Rod and Emil wrote it, started Spring Data efforts
● SDN 1.x - Embedded Neo4j, Object Mapping, Templates
● SDN 2.x - Neo4j Server, SD-Repositories
● SDN 3.x - Neo4j 2.x with Label + Schema Index Support, etc.
Now… Spring Data Neo4j 4.0● New and Shiny!
o Complete rewrite
o Neo4j Server only (for now, at least)
o Sympathetic to typical business use cases
o And … not just for Spring developers!
Spring Data Neo4j 4 - Progress● Development started Sep 2014
● Milestone 1 released end Mar 2015
● RC1 end-May 2015
● GA … mid-June 2015
Spring Data + Separate OGM● Standalone Java OGM
● Spring+
o Template methods
o Transactions
o Repositories
o Queries
o Exception Translation
o Conversion Services
Approach● Constraint: Cypher over HTTP
● Solution: Focus on performance!
Core OGM - Features● "Vampire" Metadata scanning (no reflection!)
● Optional annotations
● Mapping contexts scoped to “conversational” lifetimes:
o Application,
o HTTP session,
o HTTP request, etc.
● "Smart" object-mapping
● Variable-depth persistence
Variable-depth persistence
If you're interested in loading this...
...you're often really interested in this
Variable-depth persistenceIf your domain objects are connected….…you should be able to save them all at once - from any object.
Conference Application
Spring Data Neo4j 4.0 Spring Boot Angular.js
Application architectureStraightforward Spring Boot application:
● RESTControllers
● … depending on Services
● …... depending on Spring Repositories
● to persist POJO Entity classes
Entities and RelationshipsEntities (Labels)
● Speaker
● Conference
● Track
● Talk (Session)
● Timeslot
Relationships
● PRESENTS
● REGISTERED_FOR
● IN_TRACK
● HAS_TRACK
● AT_TIMESLOT
Conference Graph Model
Speaker
Conference
Track
Session
Timeslot
PRESENTS
REGISTERED_FORHAS_TRACK
IN_TRACKAT_TIME_SLOT
Preparing the data - CQLcreate (conference:Conference {name : 'GraphConnect 2015'})
create (track1:Track {name : 'Technical Track'})
create (track2:Track {name : 'Business Track'})
create (track3:Track {name : 'Practitioner Track'})
create (track4:Track {name : 'Beginner Track'})
create (conference)<-[:REGISTERED_FOR]-(long:Speaker {name : 'Josh Long'})
create (conference)<-[:REGISTERED_FOR]-(gonzalez:Speaker {name : 'Ignasi Gonzalez'})
create (conference)<-[:REGISTERED_FOR]-(lopez:Speaker {name : 'Ivan Lopez'})
create (conference)<-[:REGISTERED_FOR]-(rodriguez:Speaker {name : 'Anton Rodriguez'})
…etc
All the data loaded…
Configuration: Spring Boot@SpringBootApplication
@Import(PersistenceConfig.class)
public class ConferenceApplication {
public static void main(String[] args) {
SpringApplication app = new SpringApplication(ConferenceApplication.class);
app.setShowBanner(false);
app.run(args);
}
}
Configuration: Persistence@Configuration @EnableNeo4jRepositories(basepackages="app.conference.repository") @EnableTransactionManagement
public class PersistenceConfig extends Neo4jConfiguration {
@Bean public Neo4jServer neo4jServer() { return new RemoteServer("http://localhost:7474"); }
@Bean public SessionFactory getSessionFactory() { return new SessionFactory("app.conference.domain"); }
@Bean @Scope(value="session", proxyMode=ScopedProxyMode.TARGET_CLASS) public Session getSession() throws Exception { return super.getSession(); } }
Entities: Abstract Entity@JsonIdentifyInfo(generator=JSOGGenerator.class)
public abstract class Entity {
@JsonProperty("id") @GraphId private Long id;
private String name;
}
Entities: Speakerpublic class Speaker extends Entity { @Relationship(type="PRESENTS") private Set<Talk> talks; }
Entities: Talk @NodeEntity(label="Session")
public class Talk extends Entity {
@Relationship(type="AT_TIMESLOT") private Timeslot timeslot;
@Relationship(type="IN_TRACK") private Track track;
@Relationship(type="PRESENTS", direction=Relationship.INCOMING) private Set<Speaker> presenters;
}
Repositoriesinterface SpeakerRepository extends GraphRepository<Speaker> {
@Query("MATCH(s:Speaker)-[:PRESENTS]->() return s, count(*) as hits ORDER BY hits DESC")
Iterable<Map<String,Object>> speakersByNumberOfTalks();
}
Services - abstraction
public abstract class GenericCRUDService<T> implements Service<T> {
private static final int DEPTH_LIST = 0; private static final int DEPTH_ENTITY = 1;
@Override public Iterable<T> findAll() { return getRepository().findAll(DEPTH_LIST); }
@Override public T find(Long id) { return getRepository().findOne(id, DEPTH_ENTITY); }
public abstract GraphRepository<T> getRepository();
}
Services - implementation@Service
public class SpeakerService extends GenericCRUDService<Speaker> {
@Autowired private SpeakerRepository repository;
@Override public GraphRepository<Speaker> getRepository() { return repository; }
}
Controllers - abstraction@RequestMapping(value = "/api")
public abstract class Controller<T> {
public Iterable<T> list() { return getService().findAll(); }
public T create (T entity) { return getService().createOrUpdate(entity); } // … more controller methods here
public abstract Service<T> getService();
}
Controllers - implementation// controller methods get an @ResponseBody annotation by default, yay!
@RestController public class TalkController extends Controller<Talk> {
@Autowired private TalkService service;
@RequestMapping(value="/talks", method = RequestMethod.GET) public Iterable<Talk> list() { return service.findAll(); } }
Front end: Angular js ● Very simple CRUD application
● Loosely based on jHipster angular templates
● Each JSON entity (track, speaker, etc) fully supported by 5 files:
o config.js
o servicefactory.js
o controllers.js
o list.html
o detail.html
DemoConference Application
Where next?Immediately after RC1:
● Project code split
● General release mid-June
● More features planned
o Dynamic Properties
o Support for Embedded Neo4j
o Versioning (MVCC)
o Automatic hooks to pre/post-save lifecycle events
o Binary protocol …etc.
Getting startedNeo4j: http://neo4j.com/download
SDN4 Conference Application: https://github.com/neo4j-examples/sdn4-conference
Spring Data Neo4j (4)
Current Release: http://maven.springframework.org/milestone/org/springframework/data/spring-data-neo4j/4.0.0.M1
Latest Build: http://repo.spring.io/libs-snapshot/org/springframework/data/spring-data-neo4j/4.0.0.BUILD-SNAPSHOT
Code: https://github.com/spring-projects/spring-data-neo4j/tree/4.0
Documentation: http://docs.spring.io/spring-data/neo4j/docs/4.0.0.M1
GraphAwareTM
Getting help
Luanne Misquitta
Adam George
Vince Bickers
Want to track/raise an issue?
○ https://jira.spring.io/browse/DATAGRAPH
Got questions, or need some advice?
○ Check our blog:http://graphaware.com/blog/
○ Stack Overflow is your friend
graphaware.com
@graph_aware