Spring Data FalkorDB
This page describes how to integrate FalkorDB with Spring Data using spring-data-falkordb.
Overview
Spring Data FalkorDB provides JPA-style object-graph mapping for FalkorDB, enabling developers to use familiar Spring Data patterns and annotations to work with graph databases. This library makes it easy to build high-performance graph-based applications using Spring’s data access framework.
Key Features
- JPA-style Annotations: Use familiar
@Node,@Relationship,@Id,@Propertyannotations - Repository Abstractions: Implement
FalkorDBRepository<T, ID>for automatic CRUD operations - Derived Query Methods: Full support for Spring Data query methods like
findByName,findByAgeGreaterThan, etc. - Custom Queries: Write Cypher queries with
@Queryannotation and named parameters - Auto-Configuration: Enable repositories with
@EnableFalkorDBRepositories - Object-Graph Mapping: Automatic conversion between Java objects and FalkorDB graph structures
- Transaction Support: Built on Spring’s robust transaction management
- High Performance: Leverages FalkorDB’s speed with the official JFalkorDB Java client
Getting Started
Installation
Add the following dependencies to your project:
Maven
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-falkordb</artifactId>
<version>1.0.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>com.falkordb</groupId>
<artifactId>jfalkordb</artifactId>
<version>0.5.1</version>
</dependency>
Gradle
dependencies {
implementation 'org.springframework.data:spring-data-falkordb:1.0.0-SNAPSHOT'
implementation 'com.falkordb:jfalkordb:0.5.1'
}
Entity Mapping
Define your graph entities using Spring Data annotations:
@Node(labels = {"Person", "Individual"})
public class Person {
@Id
@GeneratedValue
private Long id;
@Property("full_name") // Maps to "full_name" property in FalkorDB
private String name;
private String email;
private int age;
@Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
private List<Person> friends;
@Relationship(type = "WORKS_FOR", direction = Relationship.Direction.OUTGOING)
private Company company;
// Constructors, getters, and setters...
}
@Node("Company")
public class Company {
@Id
@GeneratedValue
private Long id;
private String name;
private String industry;
@Property("employee_count")
private int employeeCount;
@Relationship(type = "EMPLOYS", direction = Relationship.Direction.INCOMING)
private List<Person> employees;
// Constructors, getters, and setters...
}
Repository Interface
Create repository interfaces extending FalkorDBRepository:
public interface PersonRepository extends FalkorDBRepository<Person, Long> {
// Derived query methods (automatically implemented)
Optional<Person> findByName(String name);
List<Person> findByAgeGreaterThan(int age);
List<Person> findByEmail(String email);
List<Person> findByNameAndAgeGreaterThan(String name, int age);
List<Person> findByNameOrEmail(String name, String email);
Page<Person> findByAgeGreaterThan(int age, Pageable pageable);
// Count and existence queries
long countByAge(int age);
boolean existsByEmail(String email);
// Custom Cypher queries with named parameters
@Query("MATCH (p:Person)-[:KNOWS]->(f:Person) WHERE p.name = $name RETURN f")
List<Person> findFriends(@Param("name") String name);
@Query("MATCH (p:Person) WHERE p.age > $minAge AND p.age < $maxAge RETURN p")
List<Person> findByAgeRange(@Param("minAge") int minAge, @Param("maxAge") int maxAge);
}
Configuration
Configure the FalkorDB connection in your Spring application:
@Configuration
@EnableFalkorDBRepositories
public class FalkorDBConfig {
@Bean
public FalkorDBClient falkorDBClient() {
Driver driver = FalkorDB.driver("localhost", 6379);
return new DefaultFalkorDBClient(driver, "social");
}
@Bean
public FalkorDBTemplate falkorDBTemplate(FalkorDBClient client,
FalkorDBMappingContext mappingContext,
FalkorDBEntityConverter converter) {
return new FalkorDBTemplate(client, mappingContext, converter);
}
}
Service Usage
Use repositories and templates in your service classes:
@Service
@Transactional
public class PersonService {
@Autowired
private PersonRepository personRepository;
@Autowired
private FalkorDBTemplate falkorDBTemplate;
public Person createPerson(String name, String email) {
Person person = new Person(name, email);
return personRepository.save(person);
}
public List<Person> findYoungAdults() {
return personRepository.findByAgeBetween(18, 30);
}
public List<Person> findConnectedPeople(int minAge) {
String cypher = """
MATCH (p:Person)-[:KNOWS]-(friend:Person)
WHERE p.age > $minAge
RETURN p, friend
""";
Map<String, Object> params = Collections.singletonMap("minAge", minAge);
return falkorDBTemplate.query(cypher, params, Person.class);
}
}
Supported Annotations
@Node
Marks a class as a graph node entity:
@Node("Person") // Single label
@Node(labels = {"Person", "Individual"}) // Multiple labels
@Node(primaryLabel = "Person") // Explicit primary label
@Id
Marks the entity identifier:
@Id
private String customId; // Assigned ID
@Id
@GeneratedValue
private Long id; // FalkorDB internal ID
@Id
@GeneratedValue(UUIDStringGenerator.class)
private String uuid; // Custom generator
@Property
Maps fields to graph properties:
@Property("full_name")
private String name; // Maps to "full_name" property
private String email; // Maps to "email" property (default)
@Interned
Marks string properties as low-cardinality, applying FalkorDB’s intern() function to optimize storage:
@Interned
private String status; // Uses intern() - ideal for limited values like "ACTIVE", "INACTIVE"
@Interned
private String country; // Uses intern() - ideal for country codes "US", "UK", "CA"
@Interned
private String category; // Uses intern() - ideal for categories like "SPORTS", "NEWS"
The @Interned annotation is useful for string properties that have a limited set of possible values (low cardinality). When a property is marked with @Interned, FalkorDB’s intern() function is automatically applied when writing to the database, which keeps only a single copy of frequently repeated string values, optimizing storage and query performance.
Use cases:
- Status codes (ACTIVE, INACTIVE, PENDING)
- Country/region codes
- Categories and types
- Enum-like string values
- Any string with a limited vocabulary
@Relationship
Maps relationships between entities:
@Relationship(type = "KNOWS", direction = Relationship.Direction.OUTGOING)
private List<Person> friends;
@Relationship(type = "WORKS_FOR", direction = Relationship.Direction.OUTGOING)
private Company company;
@Relationship(type = "EMPLOYS", direction = Relationship.Direction.INCOMING)
private List<Person> employees;
Repository Query Methods
Spring Data FalkorDB supports two types of queries:
1. Derived Query Methods (Automatically Implemented)
Define methods following Spring Data naming conventions, and the implementation is generated automatically:
Query Keywords
findBy...: Find entities matching criteriacountBy...: Count entities matching criteriaexistsBy...: Check if entities exist matching criteriadeleteBy...: Delete entities matching criteriafindFirstBy.../findTopNBy...: Limit results
Supported Comparison Operations
// Equality
findByName(String name)
findByNameNot(String name)
// Comparison
findByAgeGreaterThan(int age)
findByAgeGreaterThanEqual(int age)
findByAgeLessThan(int age)
findByAgeLessThanEqual(int age)
// String operations
findByNameContaining(String substring) // *substring*
findByNameStartingWith(String prefix) // prefix*
findByNameEndingWith(String suffix) // *suffix
findByNameLike(String pattern) // Custom pattern
findByNameNotContaining(String substring)
// Null checks
findByEmailIsNull()
findByEmailIsNotNull()
// Boolean
findByActiveTrue()
findByActiveFalse()
// Collections
findByAgeIn(Collection<Integer> ages)
findByAgeNotIn(Collection<Integer> ages)
// Logical operations
findByNameAndAge(String name, int age) // AND condition
findByNameOrEmail(String name, String email) // OR condition
// Sorting and pagination
findByAgeGreaterThan(int age, Sort sort)
findByAgeGreaterThan(int age, Pageable pageable)
// Limiting results
findFirstByOrderByCreatedAtDesc()
findTop10ByOrderByAgeDesc()
2. Custom Cypher Queries with @Query
Write custom Cypher queries for complex operations:
public interface PersonRepository extends FalkorDBRepository<Person, Long> {
// Using named parameters
@Query("MATCH (p:Person)-[:KNOWS]->(f:Person) "
+ "WHERE p.name = $name RETURN f")
List<Person> findFriends(@Param("name") String name);
// Using indexed parameters
@Query("MATCH (p:Person) WHERE p.age > $0 RETURN p")
List<Person> findOlderThan(int age);
// Count query
@Query(value = "MATCH (p:Person)-[:WORKS_FOR]->(c:Company) "
+ "WHERE c.name = $company RETURN count(p)",
count = true)
long countEmployees(@Param("company") String company);
// Exists query
@Query(value = "MATCH (p:Person {email: $email}) RETURN count(p) > 0",
exists = true)
boolean emailExists(@Param("email") String email);
// Write query (creates/updates data)
@Query(value = "MATCH (p:Person {id: $id}) "
+ "SET p.lastLogin = $time",
write = true)
void updateLastLogin(@Param("id") Long id, @Param("time") LocalDateTime time);
}
Query Method Examples
// Simple equality
List<Person> people = repository.findByName("John");
// Comparison
List<Person> adults = repository.findByAgeGreaterThanEqual(18);
// String matching
List<Person> smiths = repository.findByNameEndingWith("Smith");
// Logical AND/OR
List<Person> results = repository.findByNameAndAgeGreaterThan("John", 25);
List<Person> results = repository.findByNameOrEmail("John", "john@example.com");
// Null checks
List<Person> noEmail = repository.findByEmailIsNull();
// Collections
List<Person> youngPeople = repository.findByAgeIn(Arrays.asList(18, 19, 20));
// Counting and existence
long count = repository.countByAge(25);
boolean exists = repository.existsByEmail("test@example.com");
// Sorting
List<Person> sorted = repository.findByAgeGreaterThan(20, Sort.by("name").ascending());
// Pagination
Page<Person> page = repository.findByAgeGreaterThan(18, PageRequest.of(0, 10));
// Limiting
Optional<Person> youngest = repository.findFirstByOrderByAgeAsc();
List<Person> oldest = repository.findTop5ByOrderByAgeDesc();
// Delete
repository.deleteByAge(0); // Delete all with age = 0
Twitter Integration Example
The library includes a comprehensive Twitter-like integration test that demonstrates real-world usage patterns. This example creates a social graph with users, tweets, follows, and hashtags.
Entity Examples
TwitterUser Entity
@Node(labels = { "User", "TwitterUser" })
public class TwitterUser {
@Id @GeneratedValue
private Long id;
@Property("username") private String username;
@Property("display_name") private String displayName;
@Property("email") private String email;
@Property("bio") private String bio;
@Property("follower_count") private Integer followerCount;
@Property("verified") private Boolean verified;
@Property("created_at") private LocalDateTime createdAt;
@Relationship(value = "FOLLOWS", direction = OUTGOING)
private List<TwitterUser> following;
@Relationship(value = "POSTED", direction = OUTGOING)
private List<Tweet> tweets;
}
Tweet Entity
@Node(labels = { "Tweet" })
public class Tweet {
@Id @GeneratedValue
private Long id;
@Property("text") private String text;
@Property("created_at") private LocalDateTime createdAt;
@Property("like_count") private Integer likeCount;
@Property("retweet_count") private Integer retweetCount;
@Relationship(value = "POSTED", direction = INCOMING)
private TwitterUser author;
@Relationship(value = "HAS_HASHTAG", direction = OUTGOING)
private List<Hashtag> hashtags;
}
Advanced Configuration
Connection Pool Settings
@Bean
public FalkorDBClient falkorDBClient() {
Driver driver = FalkorDB.driver("localhost", 6379);
// Configure connection pool if needed
return new DefaultFalkorDBClient(driver, "myapp");
}
Custom Converters
@Configuration
public class FalkorDBConfig {
@Bean
public FalkorDBCustomConversions customConversions() {
return new FalkorDBCustomConversions(Arrays.asList(
new LocalDateTimeToStringConverter(),
new StringToLocalDateTimeConverter()
));
}
}
Transaction Configuration
@Configuration
@EnableTransactionManagement
public class FalkorDBTransactionConfig {
@Bean
public FalkorDBTransactionManager transactionManager(FalkorDBClient client) {
return new FalkorDBTransactionManager(client);
}
}