graphconnect europe 2016 - building spring data neo4j 4.1 applications like a superhero - luanne...
TRANSCRIPT
GraphAware®
Build Spring Data Neo4j 4 Applications like a Superhero
graphaware.com
@graph_aware, @luannem
Luanne Misquitta
Most active Spring project
Convenient data access to (No)SQL databases
Consistent API’s
Mapping POJOs, Template, Repositories
Neo4j, JPA, MongoDB, Redis, CouchBase & more
Spring Data
GraphAware®
Focus on performance
Based on a pure Java OGM [Neo4j-OGM]
3 Drivers available to connect to Neo4j
HTTP
Embedded
Bolt
Spring Data Neo4j 4.1
GraphAware®
Object Graph Mapping from any Java or JVM-based application
“Vampire” metadata scanning (no reflection!)
“Smart” object mapping
Mapping contexts tied to Session lifetimes
Application
HTTP session/request etc.
Support for default and bespoke type conversions
Repositories, Neo4jTemplate [SDN]
Custom Queries, Derived Finders [SDN]
Transactional Support
Features
GraphAware®
Persistence horizon (depth) indicates how many relationships should be traversed in the graph when loading or saving data
Default loading depth is 1
Default persistence depth is -1
Variable Depth Persistence
GraphAware®
It’s a bird, it’s a plane, it’s a superhero graph!
Node Entities - Character
GraphAware®
@NodeEntity(label = "Character")
public class Character {
@GraphId Long graphId;
…
private Long id;
private String name;
private String alias;
private String realName;
Node Entities - Relationships
GraphAware®
public class Character {…@Relationship(type = "ALLY_OF", direction = Relationship.UNDIRECTED)
@Relationship(type = "ENEMY_OF", direction = Relationship.UNDIRECTED)
@Relationship(type = "MEMBER_OF")
@Relationship(type = "STARS", direction = Relationship.INCOMING)
Set<Character> allies = new HashSet<>();
Set<Character> enemies = new HashSet<>();
Set<Team> teams = new HashSet<>();
Set<Role> roles = new HashSet<>();
Set<Game> gamesFeaturedIn = new HashSet<>();
@Relationship(type = "FEATURED_IN")
@Relationship(type = "FEATURED_IN")
Set<Comic> comicsFeaturedIn = new HashSet<>();
Node Entities - Hierarchy
GraphAware®
public class Villain extends Character{
…
}
public class Hero extends Character {
… }
@NodeEntity(label = "Hero")
@NodeEntity(label = "Villain")
Node Entities - Team
GraphAware®
@NodeEntity(label = "Team")
public class Team {
@GraphId private Long graphId;
private Long id;
private String name;
private String operationsBase;
@Relationship(type = "MEMBER_OF",
direction = "INCOMING")
private Set<Character> members = new HashSet<>();
public Team() {
}
Node Entities - Comic
GraphAware®
@NodeEntity(label = "Comic")
private Long id;
private String title;
private String author;
private String artist;
private boolean available;
private Binding binding;
@DateLong
private Date onSaleDate;
@Relationship(type = "FEATURED_IN", direction = Relationship.INCOMING)
private Set<Character> characters = new HashSet<>();
public enum Binding {
SOFT,
LEATHER,
CLOTH,
HARD
}
public class Comic {
@GraphId private Long graphId;
Node Entities - Game
GraphAware®
@NodeEntity(label = "Game")
public class Game {
@Graphid private Long graphId;
private Long id;
private String title;
private int year;
private String publisher;
private Rating rating;
private Set<Platform> platforms;
@Relationship(type = "FEATURED_IN",
direction = Relationship.INCOMING)
private Set<Character> characters = new HashSet<>();
public enum Platform {
PC,
WII,
XBOX_360,
PLAYSTATION_3
}
Node Entities
GraphAware®
@Convert(UrlConverter.class)
private URL imdbUrl;
@Relationship(type = "STARS")
private Set<Role> stars;
public class UrlConverter implements
AttributeConverter<URL, String> {
@Override
public String toGraphProperty(URL value) {
return value == null? null : value.toString();
}
@Override
public URL toEntityAttribute(String value) {
try {
return new URL(value);
} catch (MalformedURLException e) {
e.printStackTrace();
}
return null;
} }
@NodeEntity(label = "Movie")
public class Movie {
@GraphId private Long graphId;
private Long id;
private String title;
private int year;
private Rating rating;
Relationship Entities
GraphAware®
@RelationshipEntity(type = "STARS")
public class Role {
private Long id;
@StartNode private Movie movie;
@EndNode private Character character;
private String actor;
Repositories
GraphAware®
public interface CharacterRepository<T extends Character> extends GraphRepository<T> {
List<T> findByNameLike(String keyword);
@Query(" MATCH (c:Character) WHERE ID(c)={characterId} " +
"OPTIONAL MATCH (c)-[:ALLY_OF|ENEMY_OF]-(other) " +
"WITH c, collect(other) as others " +
"OPTIONAL MATCH (c)-[:MEMBER_OF|FEATURED_IN]->
()<-[:MEMBER_OF|FEATURED_IN]-(teamMember) " +
"WITH c, others + collect(teamMember) as othersWithTeam " +
"OPTIONAL MATCH (c)<-[:STARS]-()-[:STARS]->(actors) " +
"WITH othersWithTeam + collect(actors) as allOthers " +
"UNWIND allOthers as related " +
"WITH count(*) as count, related " +
"RETURN related ORDER BY count DESC")
List<Character> findRelatedCharacters(@Param("characterId") Long id);
Repositories
GraphAware®
public interface HeroRepository extends CharacterRepository<Hero> {
}
public interface VillainRepository extends CharacterRepository<Villain>{
}
Services
GraphAware®
@Service
public class CharacterService {
@Autowired CharacterRepository<Character> characterRepository;
@Autowired HeroRepository heroRepository;
@Autowired VillainRepository villainRepository;
public List<CharacterSummary> searchHeroesByKeyword(String keyword) {
return summarizeCharacter(heroRepository.findByNameLike(getKeywordParam(keyword));
}
public List<Character> findRelatedCharacters(Long id) {
return characterRepository.findRelatedCharacters(id);
}
public Character getById(Long id) {
return characterRepository.findById(id);
}
…
}
Controller
GraphAware®
@RestController
@RequestMapping("/api/")
public class CharacterController {
@Autowired CharacterService characterService;
@RequestMapping(value = "characters/{id}", method = RequestMethod.GET)
public Character getCharacterById(@PathVariable("id") Long id) {
return characterService.getById(id);
}
@RequestMapping(value = "characters/{id}/related", method = RequestMethod.GET)
public List<Character> getRelatedCharacters(@PathVariable("id") Long id) {
return characterService.findRelatedCharacters(id);
}
SDN 4.1 / Neo4j OGM Driver Configuration
GraphAware®
driver=org.neo4j.ogm.drivers.http.driver.HttpDriver
URI=http://neo4j:neo@localhost:7474
Auto configure with ogm.properties
Java Configuration
Components.configuration()
.driverConfiguration()
.setDriverClassName("org.neo4j.ogm.drivers.http.driver.HttpDriver")
.setURI("http://user:password@localhost:7474")
SDN 4.1 Neo4j Configuration
GraphAware®
@Configuration
@ComponentScan("com.graphaware.superhero")
@EnableAutoConfiguration
@EnableTransactionManagement
@EnableNeo4jRepositories("com.graphaware.superhero.repository")
public class Application extends Neo4jConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
…
SDN 4.1 Neo4j Configuration
GraphAware®
…
public class Application extends Neo4jConfiguration {
@Override
@Bean
public SessionFactory getSessionFactory() {
return new SessionFactory("com.graphaware.superhero.domain");
}
@Override
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public Session getSession() throws Exception {
return super.getSession();
}
Up, up and away!
GraphAware®
mvn clean spring-boot:run
Documentation: http://docs.spring.io/spring-data/neo4j/docs/current/reference/html/
Code: https://github.com/spring-projects/spring-data-neo4j
Sample Applications: http://github.com/neo4j-examples?query=sdn4
Where to find stuff
GraphAware®
Where to find us
GraphAware®
Luanne Misquitta
Adam George
Michal “Batman” Bachman
Vince Bickers
Getting Help
StackOverflow (spring-data-neo4j-4, neo4j-ogm)
Slack: neo4j-users (neo4j-sdn)
Email the team: [email protected]
Raise issues: https://jira.spring.io/browse/DATAGRAPH
Twitter: Follow @graph_aware for pro tips
Blog: http://graphaware.com/blog
You’re a Graph Hero!
www.graphaware.com@graph_aware
Thank you
GraphAware®