Graph databases have quickly gained recognition in fashionable software program structure, as programs more and more depend on relationships, suggestions, and linked information. From social media platforms and fraud detection programs to suggestion engines and data graphs, graph databases provide a robust strategy to mannequin and traverse complicated relationships which are exhausting to precise effectively in relational databases.
This second a part of the sequence narrows the main focus to Neo4j, the market’s most distinguished graph database engine. We’ll discover its structure, question language (Cypher), and see how Java builders can leverage Eclipse JNoSQL 1.1.8 to combine it seamlessly into Java functions.
Understanding Neo4j
Neo4j is a local graph database explicitly constructed to retailer and course of graphs effectively and successfully. It represents information as nodes (vertices) and relationships (edges), which may maintain properties. In contrast to relational databases, the place relationships are inferred by way of international keys, Neo4j treats relationships as first-class residents, leading to quicker and extra expressive graph traversals.
Among the key options that make Neo4j standard embody:
- A strong question language: Cypher
- ACID-compliant transactional mannequin
- Excessive-performance graph traversals
- Visible information browser and mature tooling
- Robust neighborhood and business assist
Meet Cypher: The Graph Question Language
Cypher is Neo4j’s declarative question language designed to precise graph patterns intuitively. Its syntax is acquainted to SQL customers however is meant to traverse nodes and relationships, not be a part of tables.
Right here’s a fast comparability:
Characteristic | SQL | Cypher |
---|---|---|
Entity Retrieval | SELECT * FROM E book | MATCH (b:E book) RETURN b |
Filtering | WHERE title=”Java” | WHERE b.title=”Java” |
Be part of/Relationship | JOIN Book_Category ON… | MATCH (b:E book)-[:is]->(c:Class) RETURN b |
Grouping & Depend | GROUP BY category_id | WITH c, rely(b) AS complete |
Schema Flexibility | Mounted schema | Property graph, extra dynamic |
Getting Began With Neo4j and Eclipse JNoSQL
Eclipse JNoSQL simplifies database integration by adhering to Jakarta EE specs — particularly, Jakarta NoSQL and Jakarta Information. On this pattern, we’ll use Java SE and showcase the way to work together with Neo4j utilizing a website mannequin of books and their classes.
First, guarantee Neo4j is operating. Use Docker to spin it up shortly:
docker run --publish=7474:7474 --publish=7687:7687 --env NEO4J_AUTH=neo4j/admin123 neo4j:5.26.3
Now, configure the connection utilizing MicroProfile Config (which helps atmosphere variable overrides):
jnosql.neo4j.uri=bolt://localhost:7687
jnosql.neo4j.username=neo4j
jnosql.neo4j.password=admin123
jnosql.graph.database=neo4j
You may overwrite any configuration because of the Twelve Software Issue. For instance, you’ll be able to replace the manufacturing password with out altering a single line of code. What you must do is about the System atmosphere:
export JNOSQL_NEO4J_PASSWORD=PRODUCTION_PASSWORD
Modeling Entities
With Neo4j configured and operating, the following step is to outline our area mannequin utilizing Jakarta NoSQL annotations. On this instance, we deal with two entities — E book
and Class
— which can type the core nodes in our graph. These lessons will exhibit the way to insert information and outline relationships utilizing Neo4j in a clear, idiomatic manner with Java.
@Entity
public class E book {
@Id
non-public String id;
@Column
non-public String title;
}
@Entity
public class Class {
@Id
non-public String id;
@Column
non-public String title;
}
Eclipse JNoSQL affords a Neo4JTemplate
, a specialization of Template
, for native Neo4j entry. This API permits direct interactions with Neo4j utilizing Cypher queries, edge creation, and entity persistence programmatically and expressively.
This is how one can encapsulate persistence logic in a primary service layer:
@ApplicationScoped
public class BookService {
non-public static closing Logger LOGGER = Logger.getLogger(BookService.class.getName());
@Inject
non-public Neo4JTemplate template;
public E book save(E book ebook) {
Optionally available discovered = template.choose(E book.class).the place("title").eq(ebook.getName()).singleResult();
return discovered.orElseGet(() -> template.insert(ebook));
}
public Class save(Class class) {
Optionally available discovered = template.choose(Class.class).the place("title").eq(class.getName()).singleResult();
return discovered.orElseGet(() -> template.insert(class));
}
}
To exhibit a full execution cycle, we use the BookApp
class, which initializes a CDI container, shops books and classes, creates edges between them, and runs Cypher queries:
public closing class BookApp {
non-public BookApp() {}
public static void major(String[] args) {
strive (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
var template = container.choose(Neo4JTemplate.class).get();
var service = container.choose(BookService.class).get();
var software program = service.save(Class.of("Software program"));
var java = service.save(Class.of("Java"));
var structure = service.save(Class.of("Structure"));
var efficiency = service.save(Class.of("Efficiency"));
var effectiveJava = service.save(E book.of("Efficient Java"));
var cleanArchitecture = service.save(E book.of("Clear Structure"));
var systemDesign = service.save(E book.of("System Design Interview"));
var javaPerformance = service.save(E book.of("Java Efficiency"));
template.edge(Edge.supply(effectiveJava).label("is").goal(java).property("relevance", 10).construct());
template.edge(Edge.supply(effectiveJava).label("is").goal(software program).property("relevance", 9).construct());
template.edge(Edge.supply(cleanArchitecture).label("is").goal(software program).property("relevance", 8).construct());
template.edge(Edge.supply(cleanArchitecture).label("is").goal(structure).property("relevance", 10).construct());
template.edge(Edge.supply(systemDesign).label("is").goal(structure).property("relevance", 9).construct());
template.edge(Edge.supply(systemDesign).label("is").goal(software program).property("relevance", 7).construct());
template.edge(Edge.supply(javaPerformance).label("is").goal(efficiency).property("relevance", 8).construct());
template.edge(Edge.supply(javaPerformance).label("is").goal(java).property("relevance", 9).construct());
System.out.println("Books in 'Structure' class:");
var architectureBooks = template.cypher("MATCH (b:E book)-[:is]->(c:Class {title: 'Structure'}) RETURN b AS ebook", Collections.emptyMap()).toList();
architectureBooks.forEach(doc -> System.out.println(" - " + doc));
System.out.println("Classes with a couple of ebook:");
var commonCategories = template.cypher("MATCH (b:E book)-[:is]->(c:Class) WITH c, rely(b) AS complete WHERE complete > 1 RETURN c", Collections.emptyMap()).toList();
commonCategories.forEach(doc -> System.out.println(" - " + doc));
var highRelevanceBooks = template.cypher("MATCH (b:E book)-[r:is]->(:Class) WHERE r.relevance >= 9 RETURN DISTINCT b", Collections.emptyMap()).toList();
System.out.println(" Books with excessive relevance:");
highRelevanceBooks.forEach(doc -> System.out.println(" - " + doc));
System.out.println(" Books with title: 'Efficient Java':");
var effectiveJavaBooks = template.cypher("MATCH (b:E book {title: $title}) RETURN b", Collections.singletonMap("title", "Efficient Java")).toList();
effectiveJavaBooks.forEach(doc -> System.out.println(" - " + doc));
}
}
}
You may as well develop relationships and execute Cypher queries. The instance under reveals the way to outline an edge between two entities utilizing the explicitly Edge
API offered by Eclipse JNoSQL. This edge represents a relationship with the label is
and features a property, relevance
to precise its significance.
Edge edge = Edge.supply(ebook).label("is").goal(class).property("relevance", 9).construct();
template.edge(edge);
After creating edges, you need to use Cypher to question the graph. For example, the next question retrieves books which have a excessive relevance relationship (>= 9) to any class:
var books = template.cypher(
"MATCH (b:E book)-[r:is]->(:Class) WHERE r.relevance >= 9 RETURN DISTINCT b",
Collections.emptyMap()
).toList();
These examples exhibit how Neo4JTemplate can persist and relate area entities and navigate and analyze graph constructions with Cypher.
Check with BookApp
within the pattern for information setup, insertion, relationship creation, and Cypher queries. After inserting that data into the database, you’ll be able to test the Ne4J dashboard:
You may as well work together with Neo4j utilizing repository interfaces. Eclipse JNoSQL helps Neo4JRepository
a Jakarta Information extension with Cypher assist:
@Repository
public interface BookRepository extends Neo4JRepository {
Optionally available findByName(String title);
@Cypher("MATCH (b:E book)-[:is]->(c:Class {title: 'Structure'}) RETURN DISTINCT b")
Record findArchitectureBooks();
@Cypher("MATCH (b:E book)-[r:is]->(:Class) WHERE r.relevance >= 9 RETURN DISTINCT b")
Record highRelevanceBooks();
}
@Repository
public interface CategoryRepository extends Neo4JRepository {
Optionally available findByName(String title);
@Cypher("MATCH (b:E book)-[:is]->(c:Class) WITH c, rely(b) AS complete WHERE complete > 1 RETURN c")
Record commonCategories();
}
The BookApp2
class demonstrates the way to use these repositories in apply by changing the low-level Neo4JTemplate
 utilization with Jakarta Information’s repository abstraction. This method simplifies the code considerably whereas permitting entry to Cypher’s expressive energy by way of annotations.
This instance not solely reveals the way to persist entities utilizing commonplace repository strategies like findByName
, but additionally the way to carry out complicated graph queries by way of the @Cypher
annotation. Moreover, the sting creation continues to be dealt with by way of GraphTemplate
, conserving the connection modeling absolutely express and underneath management.
This dual-model — repositories for area entry and templates for graph-specific relationships — affords a terrific steadiness between comfort and adaptability, making it ideally suited for complicated area fashions with wealthy relationships.
import jakarta.enterprise.inject.se.SeContainer;
import jakarta.enterprise.inject.se.SeContainerInitializer;
import org.eclipse.jnosql.mapping.graph.Edge;
import org.eclipse.jnosql.mapping.graph.GraphTemplate;
public closing class BookApp2 {
non-public BookApp2() {
}
public static void major(String[] args) {
strive (SeContainer container = SeContainerInitializer.newInstance().initialize()) {
var template = container.choose(GraphTemplate.class).get();
var bookRepository = container.choose(BookRepository.class).get();
var repository = container.choose(CategoryRepository.class).get();
var software program = repository.findByName("Software program").orElseGet(() -> repository.save(Class.of("Software program")));
var java = repository.findByName("Java").orElseGet(() -> repository.save(Class.of("Java")));
var structure = repository.findByName("Structure").orElseGet(() -> repository.save(Class.of("Structure")));
var efficiency = repository.findByName("Efficiency").orElseGet(() -> repository.save(Class.of("Efficiency")));
var effectiveJava = bookRepository.findByName("Efficient Java").orElseGet(() -> bookRepository.save(E book.of("Efficient Java")));
var cleanArchitecture = bookRepository.findByName("Clear Structure").orElseGet(() -> bookRepository.save(E book.of("Clear Structure")));
var systemDesign = bookRepository.findByName("System Design Interview").orElseGet(() -> bookRepository.save(E book.of("System Design Interview")));
var javaPerformance = bookRepository.findByName("Java Efficiency").orElseGet(() -> bookRepository.save(E book.of("Java Efficiency")));
template.edge(Edge.supply(effectiveJava).label("is").goal(java).property("relevance", 10).construct());
template.edge(Edge.supply(effectiveJava).label("is").goal(software program).property("relevance", 9).construct());
template.edge(Edge.supply(cleanArchitecture).label("is").goal(software program).property("relevance", 8).construct());
template.edge(Edge.supply(cleanArchitecture).label("is").goal(structure).property("relevance", 10).construct());
template.edge(Edge.supply(systemDesign).label("is").goal(structure).property("relevance", 9).construct());
template.edge(Edge.supply(systemDesign).label("is").goal(software program).property("relevance", 7).construct());
template.edge(Edge.supply(javaPerformance).label("is").goal(efficiency).property("relevance", 8).construct());
template.edge(Edge.supply(javaPerformance).label("is").goal(java).property("relevance", 9).construct());
System.out.println("Books in 'Structure' class:");
var architectureBooks = bookRepository.findArchitectureBooks();
architectureBooks.forEach(doc -> System.out.println(" - " + doc));
System.out.println("Classes with a couple of ebook:");
var commonCategories = repository.commonCategories();
commonCategories.forEach(doc -> System.out.println(" - " + doc));
var highRelevanceBooks = bookRepository.highRelevanceBooks();
System.out.println("Books with excessive relevance:");
highRelevanceBooks.forEach(doc -> System.out.println(" - " + doc));
var bookByName = bookRepository.queryByName("Efficient Java");
System.out.println("E book by title: " + bookByName);
}
}
}
Conclusion
Neo4j affords highly effective graph capabilities that Java builders can now entry in a clear, commonplace manner utilizing Eclipse JNoSQL and Jakarta Information. Whether or not you select to work together by way of Neo4JTemplate
or leverage Jakarta Information repositories. The mixing is easy, type-safe, and expressive. This method helps you to mannequin complicated relationships natively with out sacrificing Java idioms or developer productiveness.