{"id":32868436,"url":"https://github.com/satishbabariya/spring-data-orientdb","last_synced_at":"2025-11-09T08:02:45.708Z","repository":{"id":319262563,"uuid":"1078117340","full_name":"satishbabariya/spring-data-orientdb","owner":"satishbabariya","description":"Provide support to increase developer productivity in Java when using Neo4j. Uses familiar Spring concepts such as a template classes for core API usage and lightweight repository style data access.","archived":false,"fork":false,"pushed_at":"2025-10-17T09:28:36.000Z","size":211,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-18T12:41:47.964Z","etag":null,"topics":["ddd","framework","java","orientdb","spring","spring-data"],"latest_commit_sha":null,"homepage":"","language":"Java","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/satishbabariya.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-10-17T08:30:17.000Z","updated_at":"2025-10-17T09:32:53.000Z","dependencies_parsed_at":null,"dependency_job_id":"2238e426-ad1b-4ad8-a2a4-617b6b4c5c58","html_url":"https://github.com/satishbabariya/spring-data-orientdb","commit_stats":null,"previous_names":["satishbabariya/spring-data-orientdb"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/satishbabariya/spring-data-orientdb","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/satishbabariya%2Fspring-data-orientdb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/satishbabariya%2Fspring-data-orientdb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/satishbabariya%2Fspring-data-orientdb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/satishbabariya%2Fspring-data-orientdb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/satishbabariya","download_url":"https://codeload.github.com/satishbabariya/spring-data-orientdb/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/satishbabariya%2Fspring-data-orientdb/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":283475140,"owners_count":26841941,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-11-09T02:00:05.828Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["ddd","framework","java","orientdb","spring","spring-data"],"created_at":"2025-11-09T08:01:11.345Z","updated_at":"2025-11-09T08:02:45.694Z","avatar_url":"https://github.com/satishbabariya.png","language":"Java","readme":"# Spring Data OrientDB\n\n[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)\n[![Java](https://img.shields.io/badge/Java-17+-orange.svg)](https://openjdk.java.net/)\n[![Spring Boot](https://img.shields.io/badge/Spring%20Boot-3.3.5-brightgreen.svg)](https://spring.io/projects/spring-boot)\n[![OrientDB](https://img.shields.io/badge/OrientDB-3.2.32-blue.svg)](https://orientdb.org)\n[![GitHub](https://img.shields.io/badge/GitHub-satishbabariya-blue.svg)](https://github.com/satishbabariya/spring-data-orientdb)\n\nSpring Data OrientDB provides a familiar and consistent Spring Data interface for [OrientDB](https://orientdb.org), the powerful multi-model graph database. This library brings the full power of Spring Data's repository abstraction, query derivation, transaction management, and entity lifecycle callbacks to OrientDB's graph and document capabilities.\n\n\u003e **Note**: This is a community-driven project and is not officially supported by the Spring team. It provides Spring Data integration for OrientDB based on Spring Data Commons patterns.\n\n## 🚀 Features\n\n### Core Functionality\n- **🔄 Spring Data Repository Support**: Full implementation of Spring Data's repository abstraction with `CrudRepository`, `PagingAndSortingRepository`\n- **📝 Annotation-Based Mapping**: Declarative entity mapping with `@Vertex`, `@Edge`, `@Property`, `@Id`, `@Version`\n- **🎯 Automatic Repository Implementation**: Define repository interfaces, get thread-safe implementations automatically\n- **🔍 Query Method Derivation**: Auto-generate queries from method names (e.g., `findByUsernameAndEmail`)\n- **💾 Template Support**: Flexible `OrientDBTemplate` for custom operations and complex queries\n- **🔐 Transaction Management**: Full integration with Spring's `@Transactional` and transaction synchronization\n- **📊 Pagination \u0026 Sorting**: Native support for `Pageable` and `Sort` with efficient OrientDB queries\n\n### Advanced Features\n- **📈 Query By Example (QBE)**: Dynamic query generation using `Example\u003cT\u003e` API\n- **🔎 Custom @Query Support**: Write custom OrientDB SQL queries with named parameters\n- **📁 Named Queries**: Define reusable queries in `orientdb-named-queries.properties`\n- **👥 Auditing Support**: Automatic timestamp and user tracking with `@CreatedDate`, `@LastModifiedDate`, `@CreatedBy`, `@LastModifiedBy`\n- **🎭 Entity Lifecycle Callbacks**: `@PrePersist`, `@PostLoad`, `@PreRemove` event hooks\n- **🏗️ Schema Generation**: Automatic OrientDB vertex/edge class creation from entity definitions\n- **🔄 Type Conversion**: Bidirectional conversion between Java objects and OrientDB documents\n- **🎯 Projection Support**: Interface-based and class-based DTOs for selective data retrieval\n- **📊 Observability \u0026 Metrics**: Integration with Micrometer for operation monitoring\n- **⚡ Async Repository Support**: Non-blocking operations with `AsyncOrientDBRepository`\n- **🎨 Caching Integration**: Built-in support for Spring Cache abstraction\n\n## 📋 Requirements\n\n- **Java**: 17 or higher\n- **Spring Boot**: 3.3.5+\n- **Spring Data Commons**: 3.3.5+\n- **OrientDB**: 3.2.32+\n- **Maven**: 3.6+ or Gradle 7+\n\n## 📦 Installation\n\n### Maven\n\nAdd the dependency to your `pom.xml`:\n\n```xml\n\u003cdependency\u003e\n    \u003cgroupId\u003eorg.springframework.data\u003c/groupId\u003e\n    \u003cartifactId\u003espring-data-orientdb\u003c/artifactId\u003e\n    \u003cversion\u003e0.0.1-SNAPSHOT\u003c/version\u003e\n\u003c/dependency\u003e\n\n\u003c!-- OrientDB dependencies (if not already included) --\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.orientechnologies\u003c/groupId\u003e\n    \u003cartifactId\u003eorientdb-core\u003c/artifactId\u003e\n    \u003cversion\u003e3.2.32\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.orientechnologies\u003c/groupId\u003e\n    \u003cartifactId\u003eorientdb-client\u003c/artifactId\u003e\n    \u003cversion\u003e3.2.32\u003c/version\u003e\n\u003c/dependency\u003e\n\u003cdependency\u003e\n    \u003cgroupId\u003ecom.orientechnologies\u003c/groupId\u003e\n    \u003cartifactId\u003eorientdb-graphdb\u003c/artifactId\u003e\n    \u003cversion\u003e3.2.32\u003c/version\u003e\n\u003c/dependency\u003e\n```\n\n### Gradle\n\n```gradle\nimplementation 'org.springframework.data:spring-data-orientdb:0.0.1-SNAPSHOT'\nimplementation 'com.orientechnologies:orientdb-core:3.2.32'\nimplementation 'com.orientechnologies:orientdb-client:3.2.32'\nimplementation 'com.orientechnologies:orientdb-graphdb:3.2.32'\n```\n\n## 🔧 Configuration\n\n### Basic Configuration\n\nCreate a configuration class that extends `AbstractOrientDBConfiguration`:\n\n```java\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.orientdb.config.AbstractOrientDBConfiguration;\nimport org.springframework.data.orientdb.repository.config.EnableOrientDBRepositories;\nimport com.orientechnologies.orient.core.db.OrientDB;\nimport com.orientechnologies.orient.core.db.OrientDBConfig;\n\n@Configuration\n@EnableOrientDBRepositories(basePackages = \"com.example.repository\")\npublic class OrientDBConfiguration extends AbstractOrientDBConfiguration {\n    \n    @Override\n    protected OrientDB orientDB() {\n        // Embedded mode\n        return new OrientDB(\"embedded:./databases\", OrientDBConfig.defaultConfig());\n        \n        // Or remote mode\n        // return new OrientDB(\"remote:localhost\", OrientDBConfig.defaultConfig());\n    }\n    \n    @Override\n    protected String getDatabaseName() {\n        return \"myapp\";\n    }\n    \n    @Override\n    protected String getUsername() {\n        return \"admin\";\n    }\n    \n    @Override\n    protected String getPassword() {\n        return \"admin\";\n    }\n}\n```\n\n### Advanced Configuration with All Features\n\n```java\nimport org.springframework.context.annotation.Configuration;\nimport org.springframework.data.orientdb.config.AbstractOrientDBConfiguration;\nimport org.springframework.data.orientdb.config.EnableOrientDBAuditing;\nimport org.springframework.data.orientdb.config.EnableOrientDBCaching;\nimport org.springframework.data.orientdb.config.EnableOrientDBObservability;\nimport org.springframework.data.orientdb.config.EnableOrientDBTransactionManagement;\nimport org.springframework.data.orientdb.repository.config.EnableOrientDBRepositories;\n\n@Configuration\n@EnableOrientDBRepositories(basePackages = \"com.example.repository\")\n@EnableOrientDBAuditing(auditorAwareRef = \"auditorProvider\")\n@EnableOrientDBCaching\n@EnableOrientDBObservability\n@EnableOrientDBTransactionManagement\npublic class OrientDBConfiguration extends AbstractOrientDBConfiguration {\n    \n    @Override\n    protected OrientDB orientDB() {\n        OrientDBConfig config = OrientDBConfig.builder()\n            .addConfig(OGlobalConfiguration.DB_POOL_MIN, 5)\n            .addConfig(OGlobalConfiguration.DB_POOL_MAX, 20)\n            .build();\n        return new OrientDB(\"remote:localhost\", config);\n    }\n    \n    @Override\n    protected String getDatabaseName() {\n        return \"production_db\";\n    }\n    \n    @Override\n    protected String getUsername() {\n        return System.getenv(\"ORIENTDB_USER\");\n    }\n    \n    @Override\n    protected String getPassword() {\n        return System.getenv(\"ORIENTDB_PASSWORD\");\n    }\n    \n    @Bean\n    public AuditorAware\u003cString\u003e auditorProvider() {\n        return () -\u003e Optional.of(SecurityContextHolder.getContext()\n            .getAuthentication().getName());\n    }\n}\n```\n\n### application.properties\n\n```properties\n# OrientDB Configuration\norientdb.url=remote:localhost\norientdb.database=myapp\norientdb.username=admin\norientdb.password=admin\n\n# Connection Pool\norientdb.pool.min=5\norientdb.pool.max=20\n\n# Schema Generation\norientdb.schema.auto-generate=true\n\n# Logging\nlogging.level.org.springframework.data.orientdb=DEBUG\n```\n\n## 🎯 Quick Start Guide\n\n### 1. Define Your Entities\n\n```java\nimport org.springframework.data.orientdb.core.schema.*;\nimport com.orientechnologies.orient.core.id.ORID;\n\n@Vertex(\"Person\")\npublic class Person {\n    \n    @Id\n    private ORID id;\n    \n    private String firstName;\n    \n    private String lastName;\n    \n    @Property(\"email_address\")\n    private String email;\n    \n    private Integer age;\n    \n    private String department;\n    \n    private Boolean active;\n    \n    @Edge(type = \"KNOWS\", direction = Edge.Direction.OUTGOING)\n    private List\u003cPerson\u003e friends;\n    \n    @Edge(type = \"WORKS_FOR\", direction = Edge.Direction.OUTGOING)\n    private Company company;\n    \n    @Version\n    private Integer version;\n    \n    @CreatedDate\n    private LocalDateTime createdAt;\n    \n    @LastModifiedDate\n    private LocalDateTime updatedAt;\n    \n    @CreatedBy\n    private String createdBy;\n    \n    @LastModifiedBy\n    private String lastModifiedBy;\n    \n    // Lifecycle callbacks\n    @PrePersist\n    public void prePersist() {\n        if (active == null) {\n            active = true;\n        }\n    }\n    \n    @PostLoad\n    public void postLoad() {\n        // Initialize transient fields, etc.\n    }\n    \n    @PreRemove\n    public void preRemove() {\n        // Cleanup before deletion\n    }\n    \n    // Constructors, getters, setters, toString, equals, hashCode...\n}\n\n@Vertex(\"Company\")\npublic class Company {\n    \n    @Id\n    private ORID id;\n    \n    private String name;\n    \n    private String industry;\n    \n    @Edge(type = \"WORKS_FOR\", direction = Edge.Direction.INCOMING)\n    private List\u003cPerson\u003e employees;\n    \n    // Constructors, getters, setters...\n}\n```\n\n### 2. Create Repository Interfaces\n\n```java\nimport org.springframework.data.orientdb.repository.OrientDBRepository;\nimport org.springframework.data.orientdb.repository.query.Query;\nimport org.springframework.data.repository.query.Param;\nimport org.springframework.data.domain.Page;\nimport org.springframework.data.domain.Pageable;\nimport com.orientechnologies.orient.core.id.ORID;\n\npublic interface PersonRepository extends OrientDBRepository\u003cPerson, ORID\u003e {\n    \n    // ========== Derived Query Methods (Auto-generated) ==========\n    \n    Person findByEmail(String email);\n    \n    List\u003cPerson\u003e findByFirstNameAndLastName(String firstName, String lastName);\n    \n    List\u003cPerson\u003e findByAgeGreaterThan(Integer age);\n    \n    List\u003cPerson\u003e findByAgeGreaterThanEqual(Integer age);\n    \n    List\u003cPerson\u003e findByAgeLessThan(Integer age);\n    \n    List\u003cPerson\u003e findByDepartmentAndActive(String department, Boolean active);\n    \n    List\u003cPerson\u003e findByFirstNameContaining(String name);\n    \n    List\u003cPerson\u003e findByFirstNameStartingWith(String prefix);\n    \n    List\u003cPerson\u003e findByFirstNameEndingWith(String suffix);\n    \n    List\u003cPerson\u003e findByAgeIn(Collection\u003cInteger\u003e ages);\n    \n    List\u003cPerson\u003e findByActiveTrue();\n    \n    List\u003cPerson\u003e findByActiveFalse();\n    \n    List\u003cPerson\u003e findByEmailIsNull();\n    \n    List\u003cPerson\u003e findByEmailIsNotNull();\n    \n    // Sorting\n    List\u003cPerson\u003e findByDepartmentOrderByLastNameAsc(String department);\n    \n    // Limiting\n    List\u003cPerson\u003e findTop10ByOrderByCreatedAtDesc();\n    \n    Person findFirstByOrderByCreatedAtAsc();\n    \n    // Count and Exists\n    long countByDepartment(String department);\n    \n    boolean existsByEmail(String email);\n    \n    // Delete operations\n    long deleteByInactiveSince(LocalDateTime date);\n    \n    void removeByActive(Boolean active);\n    \n    // ========== Custom Queries ==========\n    \n    @Query(\"SELECT FROM Person WHERE firstName LIKE :pattern OR lastName LIKE :pattern\")\n    List\u003cPerson\u003e searchByName(@Param(\"pattern\") String pattern);\n    \n    @Query(\"SELECT expand(out('KNOWS')) FROM Person WHERE @rid = :personId\")\n    List\u003cPerson\u003e findFriends(@Param(\"personId\") ORID personId);\n    \n    @Query(\"SELECT FROM Person WHERE department = :dept AND age \u003e :minAge\")\n    Page\u003cPerson\u003e findByDepartmentAndAgeGreaterThan(\n        @Param(\"dept\") String department, \n        @Param(\"minAge\") Integer minAge, \n        Pageable pageable\n    );\n    \n    @Query(\"MATCH {class: Person, as: p}-WORKS_FOR-\u003e{class: Company, as: c, where: (name = :companyName)} RETURN p\")\n    List\u003cPerson\u003e findEmployeesByCompany(@Param(\"companyName\") String companyName);\n    \n    // ========== Pagination \u0026 Sorting ==========\n    \n    Page\u003cPerson\u003e findByDepartment(String department, Pageable pageable);\n    \n    Slice\u003cPerson\u003e findByActive(Boolean active, Pageable pageable);\n    \n    // ========== Query By Example ==========\n    \n    // Inherited from OrientDBRepository:\n    // \u003cS extends Person\u003e Optional\u003cS\u003e findOne(Example\u003cS\u003e example);\n    // \u003cS extends Person\u003e Iterable\u003cS\u003e findAll(Example\u003cS\u003e example);\n    // \u003cS extends Person\u003e Page\u003cS\u003e findAll(Example\u003cS\u003e example, Pageable pageable);\n}\n```\n\n### 3. Use Repositories in Services\n\n```java\nimport org.springframework.stereotype.Service;\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.data.domain.*;\n\n@Service\n@Transactional\npublic class PersonService {\n    \n    private final PersonRepository personRepository;\n    \n    public PersonService(PersonRepository personRepository) {\n        this.personRepository = personRepository;\n    }\n    \n    // Create\n    public Person createPerson(String firstName, String lastName, String email) {\n        Person person = new Person();\n        person.setFirstName(firstName);\n        person.setLastName(lastName);\n        person.setEmail(email);\n        person.setActive(true);\n        return personRepository.save(person);\n    }\n    \n    // Read\n    public Optional\u003cPerson\u003e findById(ORID id) {\n        return personRepository.findById(id);\n    }\n    \n    public Person findByEmail(String email) {\n        return personRepository.findByEmail(email);\n    }\n    \n    // Update\n    public Person updatePerson(ORID id, String newEmail) {\n        Person person = personRepository.findById(id)\n            .orElseThrow(() -\u003e new EntityNotFoundException(\"Person not found\"));\n        person.setEmail(newEmail);\n        return personRepository.save(person);\n    }\n    \n    // Delete\n    public void deletePerson(ORID id) {\n        personRepository.deleteById(id);\n    }\n    \n    // Pagination\n    public Page\u003cPerson\u003e getAllPersons(int page, int size) {\n        Pageable pageable = PageRequest.of(page, size, \n            Sort.by(\"lastName\").ascending().and(Sort.by(\"firstName\")));\n        return personRepository.findAll(pageable);\n    }\n    \n    // Query By Example\n    public List\u003cPerson\u003e findSimilarPersons(Person probe) {\n        ExampleMatcher matcher = ExampleMatcher.matching()\n            .withIgnorePaths(\"id\", \"version\")\n            .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)\n            .withIgnoreCase();\n        Example\u003cPerson\u003e example = Example.of(probe, matcher);\n        return personRepository.findAll(example);\n    }\n    \n    // Custom query\n    public List\u003cPerson\u003e searchPersons(String searchTerm) {\n        return personRepository.searchByName(\"%\" + searchTerm + \"%\");\n    }\n    \n    // Graph traversal\n    public List\u003cPerson\u003e getFriends(ORID personId) {\n        return personRepository.findFriends(personId);\n    }\n    \n    // Batch operations\n    public List\u003cPerson\u003e createMultiplePersons(List\u003cPerson\u003e persons) {\n        return personRepository.saveAll(persons);\n    }\n    \n    // Count\n    public long countByDepartment(String department) {\n        return personRepository.countByDepartment(department);\n    }\n    \n    // Exists\n    public boolean emailExists(String email) {\n        return personRepository.existsByEmail(email);\n    }\n}\n```\n\n### 4. Using OrientDBTemplate for Advanced Operations\n\n```java\nimport org.springframework.data.orientdb.core.OrientDBTemplate;\nimport org.springframework.stereotype.Service;\n\n@Service\npublic class AdvancedPersonService {\n    \n    private final OrientDBTemplate orientDBTemplate;\n    \n    public AdvancedPersonService(OrientDBTemplate orientDBTemplate) {\n        this.orientDBTemplate = orientDBTemplate;\n    }\n    \n    // Execute custom query\n    public List\u003cPerson\u003e findActivePersonsInDepartment(String department) {\n        String query = \"SELECT FROM Person WHERE department = ? AND active = true ORDER BY lastName\";\n        return orientDBTemplate.query(query, Person.class, department);\n    }\n    \n    // Single result query\n    public Optional\u003cPerson\u003e findPersonByEmailTemplate(String email) {\n        String query = \"SELECT FROM Person WHERE email = ?\";\n        return orientDBTemplate.querySingle(query, Person.class, email);\n    }\n    \n    // Execute command\n    public void createEdgeBetweenPersons(ORID personId1, ORID personId2) {\n        orientDBTemplate.execute(session -\u003e {\n            String command = \"CREATE EDGE KNOWS FROM ? TO ?\";\n            session.command(command, personId1, personId2);\n            return null;\n        });\n    }\n    \n    // Complex graph query\n    public List\u003cPerson\u003e findFriendsOfFriends(ORID personId) {\n        String query = \"SELECT expand(out('KNOWS').out('KNOWS')) FROM Person WHERE @rid = ?\";\n        return orientDBTemplate.query(query, Person.class, personId);\n    }\n    \n    // Bulk insert\n    public void bulkInsert(List\u003cPerson\u003e persons) {\n        orientDBTemplate.execute(session -\u003e {\n            persons.forEach(person -\u003e {\n                OVertex vertex = session.newVertex(\"Person\");\n                vertex.setProperty(\"firstName\", person.getFirstName());\n                vertex.setProperty(\"lastName\", person.getLastName());\n                vertex.setProperty(\"email\", person.getEmail());\n                vertex.save();\n            });\n            session.commit();\n            return null;\n        });\n    }\n    \n    // Native OrientDB session access\n    public void performComplexOperation() {\n        orientDBTemplate.execute(session -\u003e {\n            // Full access to OrientDB session\n            OResultSet results = session.query(\"MATCH ...\");\n            // Process results...\n            session.commit();\n            return null;\n        });\n    }\n}\n```\n\n## 📚 Annotation Reference\n\n### Entity Annotations\n\n#### `@Vertex`\nMarks a class as an OrientDB vertex entity.\n\n```java\n@Vertex(\"User\")           // Maps to OrientDB class \"User\"\n@Vertex                   // Uses class name as vertex class\npublic class User { }\n```\n\n#### `@Edge`\nDefines graph relationships between vertices.\n\n```java\n@Edge(type = \"KNOWS\", direction = Direction.OUTGOING)\nprivate List\u003cPerson\u003e friends;\n\n@Edge(type = \"WORKS_FOR\", direction = Direction.INCOMING)\nprivate Company company;\n\n@Edge(type = \"MANAGES\", direction = Direction.BOTH)\nprivate List\u003cPerson\u003e managementRelationships;\n```\n\n**Direction Options:**\n- `Direction.OUTGOING`: Outgoing edges from this vertex\n- `Direction.INCOMING`: Incoming edges to this vertex\n- `Direction.BOTH`: Bidirectional edges\n\n#### `@Id`\nMarks the identifier field (typically `ORID`).\n\n```java\n@Id\nprivate ORID id;\n```\n\n#### `@Property`\nCustomizes property name mapping.\n\n```java\n@Property(\"user_name\")\nprivate String username;\n\n@Property(\"email_address\")\nprivate String email;\n```\n\n#### `@Version`\nEnables optimistic locking with version checking.\n\n```java\n@Version\nprivate Integer version;\n```\n\n### Auditing Annotations\n\nEnable with `@EnableOrientDBAuditing` on your configuration class.\n\n```java\n@CreatedDate\nprivate LocalDateTime createdAt;\n\n@LastModifiedDate\nprivate LocalDateTime updatedAt;\n\n@CreatedBy\nprivate String createdBy;\n\n@LastModifiedBy\nprivate String lastModifiedBy;\n```\n\n### Lifecycle Callback Annotations\n\n```java\n@PrePersist\npublic void beforeSave() {\n    // Called before entity is saved\n    this.slug = generateSlug(this.title);\n}\n\n@PostLoad\npublic void afterLoad() {\n    // Called after entity is loaded from database\n    this.initialized = true;\n}\n\n@PreRemove\npublic void beforeDelete() {\n    // Called before entity is deleted\n    cleanupResources();\n}\n```\n\n### Query Annotation\n\n```java\n@Query(\"SELECT FROM Person WHERE age \u003e :minAge AND department = :dept\")\nList\u003cPerson\u003e findByAgeAndDepartment(\n    @Param(\"minAge\") Integer age, \n    @Param(\"dept\") String department\n);\n```\n\n## 🔍 Query Method Naming Convention\n\nSpring Data OrientDB supports automatic query derivation from method names following Spring Data conventions:\n\n### Supported Keywords\n\n| Keyword | Sample | OrientDB SQL |\n|---------|--------|-------------|\n| `findBy` | `findByLastName(String name)` | `WHERE lastName = ?` |\n| `And` | `findByFirstNameAndLastName(...)` | `WHERE firstName = ? AND lastName = ?` |\n| `Or` | `findByFirstNameOrLastName(...)` | `WHERE firstName = ? OR lastName = ?` |\n| `GreaterThan` | `findByAgeGreaterThan(Integer age)` | `WHERE age \u003e ?` |\n| `LessThan` | `findByAgeLessThan(Integer age)` | `WHERE age \u003c ?` |\n| `GreaterThanEqual` | `findByAgeGreaterThanEqual(...)` | `WHERE age \u003e= ?` |\n| `LessThanEqual` | `findByAgeLessThanEqual(...)` | `WHERE age \u003c= ?` |\n| `Between` | `findByAgeBetween(Integer from, Integer to)` | `WHERE age BETWEEN ? AND ?` |\n| `Like` | `findByFirstNameLike(String pattern)` | `WHERE firstName LIKE ?` |\n| `NotLike` | `findByFirstNameNotLike(...)` | `WHERE firstName NOT LIKE ?` |\n| `StartingWith` | `findByFirstNameStartingWith(...)` | `WHERE firstName LIKE '?%'` |\n| `EndingWith` | `findByFirstNameEndingWith(...)` | `WHERE firstName LIKE '%?'` |\n| `Containing` | `findByFirstNameContaining(...)` | `WHERE firstName LIKE '%?%'` |\n| `In` | `findByAgeIn(Collection\u003cInteger\u003e ages)` | `WHERE age IN ?` |\n| `NotIn` | `findByAgeNotIn(...)` | `WHERE age NOT IN ?` |\n| `True` | `findByActiveTrue()` | `WHERE active = true` |\n| `False` | `findByActiveFalse()` | `WHERE active = false` |\n| `IsNull` | `findByEmailIsNull()` | `WHERE email IS NULL` |\n| `IsNotNull` | `findByEmailIsNotNull()` | `WHERE email IS NOT NULL` |\n| `OrderBy` | `findByDepartmentOrderByLastNameAsc(...)` | `ORDER BY lastName ASC` |\n| `Top/First` | `findTop10ByOrderByAgeDesc()` | `LIMIT 10` |\n| `Distinct` | `findDistinctByLastName(...)` | `SELECT DISTINCT` |\n| `countBy` | `countByDepartment(String dept)` | `SELECT count(*) WHERE ...` |\n| `existsBy` | `existsByEmail(String email)` | Returns boolean |\n| `deleteBy` | `deleteByActive(Boolean active)` | `DELETE WHERE ...` |\n| `removeBy` | `removeByDepartment(String dept)` | `DELETE WHERE ...` |\n\n## 🔐 Transaction Management\n\nSpring Data OrientDB fully integrates with Spring's transaction management:\n\n```java\nimport org.springframework.transaction.annotation.Transactional;\nimport org.springframework.transaction.annotation.Propagation;\nimport org.springframework.transaction.annotation.Isolation;\n\n@Service\n@Transactional\npublic class TransactionalService {\n    \n    @Autowired\n    private PersonRepository personRepository;\n    \n    @Autowired\n    private CompanyRepository companyRepository;\n    \n    // Method-level transaction\n    @Transactional\n    public void createPersonWithCompany(Person person, Company company) {\n        companyRepository.save(company);\n        person.setCompany(company);\n        personRepository.save(person);\n        // Both operations committed together\n    }\n    \n    // Read-only transaction (optimization)\n    @Transactional(readOnly = true)\n    public Person findPerson(ORID id) {\n        return personRepository.findById(id)\n            .orElseThrow(() -\u003e new EntityNotFoundException());\n    }\n    \n    // Propagation control\n    @Transactional(propagation = Propagation.REQUIRES_NEW)\n    public void independentTransaction() {\n        // Runs in new transaction, independent of caller\n    }\n    \n    // Rollback configuration\n    @Transactional(rollbackFor = Exception.class)\n    public void methodThatMayFail() {\n        // Rolls back on any exception\n    }\n    \n    // Timeout configuration\n    @Transactional(timeout = 30)\n    public void longRunningOperation() {\n        // Transaction times out after 30 seconds\n    }\n}\n```\n\n## 📊 Pagination and Sorting\n\n### Basic Pagination\n\n```java\nimport org.springframework.data.domain.*;\n\n// Simple pagination\nPageable pageable = PageRequest.of(0, 20); // Page 0, size 20\nPage\u003cPerson\u003e page = personRepository.findAll(pageable);\n\nSystem.out.println(\"Total elements: \" + page.getTotalElements());\nSystem.out.println(\"Total pages: \" + page.getTotalPages());\nSystem.out.println(\"Current page: \" + page.getNumber());\nSystem.out.println(\"Page size: \" + page.getSize());\nSystem.out.println(\"Has next: \" + page.hasNext());\n\n// Iterate results\npage.getContent().forEach(person -\u003e {\n    System.out.println(person.getFirstName());\n});\n```\n\n### Pagination with Sorting\n\n```java\n// Single field sort\nSort sort = Sort.by(\"lastName\").ascending();\nPageable pageable = PageRequest.of(0, 20, sort);\n\n// Multiple field sort\nSort sort = Sort.by(\"lastName\").ascending()\n                .and(Sort.by(\"firstName\").ascending());\nPageable pageable = PageRequest.of(0, 20, sort);\n\n// Sort with direction\nSort sort = Sort.by(Sort.Direction.DESC, \"createdAt\");\n\n// Complex sorting\nSort sort = Sort.by(\"department\").ascending()\n                .and(Sort.by(\"age\").descending())\n                .and(Sort.by(\"lastName\").ascending());\n```\n\n### Dynamic Sorting\n\n```java\npublic Page\u003cPerson\u003e searchWithDynamicSort(\n    String department, \n    int page, \n    int size,\n    String sortBy, \n    String direction\n) {\n    Sort.Direction sortDirection = direction.equalsIgnoreCase(\"asc\") \n        ? Sort.Direction.ASC \n        : Sort.Direction.DESC;\n    \n    Pageable pageable = PageRequest.of(\n        page, \n        size, \n        Sort.by(sortDirection, sortBy)\n    );\n    \n    return personRepository.findByDepartment(department, pageable);\n}\n```\n\n## 🔎 Query By Example (QBE)\n\nQuery By Example provides a simple way to create dynamic queries:\n\n```java\nimport org.springframework.data.domain.Example;\nimport org.springframework.data.domain.ExampleMatcher;\n\n// Simple example\nPerson probe = new Person();\nprobe.setDepartment(\"Engineering\");\nprobe.setActive(true);\n\nExample\u003cPerson\u003e example = Example.of(probe);\nList\u003cPerson\u003e results = personRepository.findAll(example);\n\n// Custom matcher\nPerson probe = new Person();\nprobe.setFirstName(\"john\");\nprobe.setDepartment(\"Eng\");\n\nExampleMatcher matcher = ExampleMatcher.matching()\n    .withIgnorePaths(\"id\", \"version\", \"createdAt\")\n    .withStringMatcher(ExampleMatcher.StringMatcher.CONTAINING)\n    .withIgnoreCase()\n    .withIgnoreNullValues();\n\nExample\u003cPerson\u003e example = Example.of(probe, matcher);\nList\u003cPerson\u003e results = personRepository.findAll(example);\n\n// Field-specific matching\nExampleMatcher matcher = ExampleMatcher.matching()\n    .withMatcher(\"firstName\", ExampleMatcher.GenericPropertyMatchers.startsWith().ignoreCase())\n    .withMatcher(\"email\", ExampleMatcher.GenericPropertyMatchers.exact())\n    .withIgnorePaths(\"version\");\n```\n\n## 🎭 Projection Support\n\n### Interface-Based Projections\n\n```java\n// Closed projection (only specified properties)\npublic interface PersonSummary {\n    String getFirstName();\n    String getLastName();\n    String getEmail();\n    \n    // Computed property\n    @Value(\"#{target.firstName + ' ' + target.lastName}\")\n    String getFullName();\n}\n\n// Usage\npublic interface PersonRepository extends OrientDBRepository\u003cPerson, ORID\u003e {\n    List\u003cPersonSummary\u003e findByDepartment(String department);\n    \n    \u003cT\u003e List\u003cT\u003e findByActive(Boolean active, Class\u003cT\u003e type);\n}\n\n// In service\nList\u003cPersonSummary\u003e summaries = personRepository.findByDepartment(\"Engineering\");\nsummaries.forEach(s -\u003e System.out.println(s.getFullName()));\n\n// Dynamic projection\nList\u003cPersonSummary\u003e summaries = personRepository.findByActive(true, PersonSummary.class);\n```\n\n### Class-Based Projections (DTOs)\n\n```java\npublic class PersonDTO {\n    private String firstName;\n    private String lastName;\n    private String email;\n    \n    public PersonDTO(String firstName, String lastName, String email) {\n        this.firstName = firstName;\n        this.lastName = lastName;\n        this.email = email;\n    }\n    \n    // Getters, setters...\n}\n\n// Repository method\npublic interface PersonRepository extends OrientDBRepository\u003cPerson, ORID\u003e {\n    List\u003cPersonDTO\u003e findByDepartment(String department);\n}\n```\n\n## 📈 Observability and Metrics\n\nEnable observability to track repository operations:\n\n```java\n@Configuration\n@EnableOrientDBObservability\npublic class ObservabilityConfig {\n    \n    @Bean\n    public MeterRegistry meterRegistry() {\n        return new SimpleMeterRegistry();\n    }\n}\n```\n\n**Metrics collected:**\n- `orientdb.repository.invocations`: Count of repository method calls\n- `orientdb.repository.execution.time`: Execution time of repository methods\n- `orientdb.query.execution.time`: Query execution times\n- `orientdb.connection.active`: Active database connections\n- `orientdb.transaction.commit`: Successful transaction commits\n- `orientdb.transaction.rollback`: Transaction rollbacks\n\n## 🔄 Async Repository Support\n\nFor non-blocking operations:\n\n```java\nimport org.springframework.data.orientdb.repository.AsyncOrientDBRepository;\nimport java.util.concurrent.CompletableFuture;\n\npublic interface PersonAsyncRepository extends AsyncOrientDBRepository\u003cPerson, ORID\u003e {\n    \n    CompletableFuture\u003cPerson\u003e findByEmail(String email);\n    \n    CompletableFuture\u003cList\u003cPerson\u003e\u003e findByDepartment(String department);\n    \n    CompletableFuture\u003cPerson\u003e save(Person person);\n}\n\n// Usage\n@Service\npublic class AsyncPersonService {\n    \n    @Autowired\n    private PersonAsyncRepository repository;\n    \n    public CompletableFuture\u003cPerson\u003e createPersonAsync(Person person) {\n        return repository.save(person)\n            .thenApply(saved -\u003e {\n                // Process saved person\n                return saved;\n            });\n    }\n    \n    public CompletableFuture\u003cList\u003cPerson\u003e\u003e searchMultipleDepartments() {\n        CompletableFuture\u003cList\u003cPerson\u003e\u003e eng = \n            repository.findByDepartment(\"Engineering\");\n        CompletableFuture\u003cList\u003cPerson\u003e\u003e sales = \n            repository.findByDepartment(\"Sales\");\n        \n        return eng.thenCombine(sales, (engineers, salespeople) -\u003e {\n            List\u003cPerson\u003e combined = new ArrayList\u003c\u003e(engineers);\n            combined.addAll(salespeople);\n            return combined;\n        });\n    }\n}\n```\n\n## 🏗️ Schema Generation\n\nAutomatic schema generation from entity classes:\n\n```java\n@Configuration\n@EnableOrientDBRepositories\npublic class OrientDBConfig extends AbstractOrientDBConfiguration {\n    \n    // ... connection configuration ...\n    \n    @Bean\n    public SchemaGenerator schemaGenerator() {\n        return new SchemaGenerator();\n    }\n    \n    @Bean\n    @DependsOn(\"schemaGenerator\")\n    public SchemaGeneratorInitializer schemaInitializer(\n        SchemaGenerator generator,\n        OrientDBMappingContext context\n    ) {\n        return new SchemaGeneratorInitializer(generator, context);\n    }\n}\n```\n\nThe schema generator will:\n1. Create vertex classes for `@Vertex` annotated entities\n2. Create properties based on entity fields\n3. Create indexes for `@Id` and `@Version` fields\n4. Set up relationships for `@Edge` annotations\n5. Configure constraints and data types\n\n## 🧪 Testing\n\n### Integration Test Example\n\n```java\nimport org.springframework.boot.test.context.SpringBootTest;\nimport org.springframework.test.context.junit.jupiter.SpringExtension;\nimport org.junit.jupiter.api.Test;\nimport org.junit.jupiter.api.extension.ExtendWith;\n\n@ExtendWith(SpringExtension.class)\n@SpringBootTest\npublic class PersonRepositoryIT {\n    \n    @Autowired\n    private PersonRepository personRepository;\n    \n    @Test\n    public void testCreateAndFindPerson() {\n        // Create\n        Person person = new Person();\n        person.setFirstName(\"John\");\n        person.setLastName(\"Doe\");\n        person.setEmail(\"john.doe@example.com\");\n        \n        Person saved = personRepository.save(person);\n        assertNotNull(saved.getId());\n        \n        // Find\n        Person found = personRepository.findByEmail(\"john.doe@example.com\");\n        assertNotNull(found);\n        assertEquals(\"John\", found.getFirstName());\n        assertEquals(\"Doe\", found.getLastName());\n    }\n    \n    @Test\n    public void testPagination() {\n        // Create test data\n        for (int i = 0; i \u003c 50; i++) {\n            Person person = new Person();\n            person.setFirstName(\"Person\" + i);\n            person.setLastName(\"Test\");\n            personRepository.save(person);\n        }\n        \n        // Test pagination\n        Pageable pageable = PageRequest.of(0, 10);\n        Page\u003cPerson\u003e page = personRepository.findAll(pageable);\n        \n        assertEquals(10, page.getContent().size());\n        assertEquals(50, page.getTotalElements());\n        assertEquals(5, page.getTotalPages());\n    }\n    \n    @Test\n    @Transactional\n    public void testTransactionalRollback() {\n        Person person = new Person();\n        person.setFirstName(\"Test\");\n        person.setLastName(\"Rollback\");\n        \n        personRepository.save(person);\n        \n        // Force exception to trigger rollback\n        throw new RuntimeException(\"Rollback test\");\n        \n        // Person should not be saved due to rollback\n    }\n}\n```\n\n### Unit Test with Mocking\n\n```java\nimport org.mockito.Mock;\nimport org.mockito.junit.jupiter.MockitoExtension;\nimport static org.mockito.Mockito.*;\n\n@ExtendWith(MockitoExtension.class)\npublic class PersonServiceTest {\n    \n    @Mock\n    private PersonRepository personRepository;\n    \n    @InjectMocks\n    private PersonService personService;\n    \n    @Test\n    public void testCreatePerson() {\n        Person person = new Person();\n        person.setFirstName(\"John\");\n        person.setLastName(\"Doe\");\n        \n        when(personRepository.save(any(Person.class)))\n            .thenReturn(person);\n        \n        Person created = personService.createPerson(\"John\", \"Doe\", \"john@example.com\");\n        \n        assertNotNull(created);\n        verify(personRepository, times(1)).save(any(Person.class));\n    }\n}\n```\n\n## 🎯 Best Practices\n\n### 1. Entity Design\n- Always use `@Id` with `ORID` type\n- Use `@Version` for entities that may be updated concurrently\n- Keep entities lightweight; use projections for read-heavy operations\n- Use lifecycle callbacks for computed values and validation\n\n### 2. Repository Design\n- Prefer derived queries over `@Query` when possible\n- Use `@Query` for complex graph traversals and joins\n- Create custom repository fragments for complex logic\n- Use projections to limit data transfer\n\n### 3. Transaction Management\n- Use `@Transactional` at service layer, not repository\n- Mark read-only operations with `readOnly = true`\n- Configure appropriate timeout values for long operations\n- Handle transaction exceptions properly\n\n### 4. Performance Optimization\n- Use pagination for large result sets\n- Enable caching for frequently accessed data\n- Use indexes on frequently queried fields\n- Consider async repositories for parallel operations\n- Use projections instead of loading full entities\n\n### 5. Connection Management\n- Configure appropriate pool sizes\n- Monitor active connections with metrics\n- Close sessions properly (handled automatically by framework)\n- Use connection pooling in production\n\n### 6. Error Handling\n\n```java\n@Service\npublic class RobustPersonService {\n    \n    @Autowired\n    private PersonRepository personRepository;\n    \n    public Optional\u003cPerson\u003e findPersonSafely(ORID id) {\n        try {\n            return personRepository.findById(id);\n        } catch (ODatabaseException e) {\n            log.error(\"Database error finding person: \" + id, e);\n            return Optional.empty();\n        } catch (Exception e) {\n            log.error(\"Unexpected error finding person: \" + id, e);\n            throw new ServiceException(\"Failed to find person\", e);\n        }\n    }\n    \n    @Transactional\n    public Person updatePersonWithRetry(ORID id, String newEmail) {\n        int maxRetries = 3;\n        int retryCount = 0;\n        \n        while (retryCount \u003c maxRetries) {\n            try {\n                Person person = personRepository.findById(id)\n                    .orElseThrow(() -\u003e new EntityNotFoundException());\n                person.setEmail(newEmail);\n                return personRepository.save(person);\n            } catch (OConcurrentModificationException e) {\n                retryCount++;\n                if (retryCount \u003e= maxRetries) {\n                    throw new ServiceException(\"Max retries exceeded\", e);\n                }\n                // Wait before retry\n                Thread.sleep(100 * retryCount);\n            }\n        }\n        throw new ServiceException(\"Update failed\");\n    }\n}\n```\n\n## 🔧 Troubleshooting\n\n### Common Issues\n\n**Issue: \"No OrientDB session found\"**\n```\nSolution: Ensure @Transactional is present or use OrientDBTemplate\n```\n\n**Issue: \"Optimistic locking failed\"**\n```\nSolution: Add retry logic or refresh entity before update\n```\n\n**Issue: \"Connection pool exhausted\"**\n```\nSolution: Increase pool size or check for connection leaks\n```\n\n**Issue: \"Query method not working\"**\n```\nSolution: Verify method naming follows conventions\nCheck entity field names match method parameters\n```\n\n### Enable Debug Logging\n\n```properties\nlogging.level.org.springframework.data.orientdb=DEBUG\nlogging.level.com.orientechnologies.orient=DEBUG\n```\n\n## 📊 Architecture Overview\n\n```\n┌─────────────────────────────────────────┐\n│         Application Layer               │\n│    (Services, Controllers, etc.)        │\n└────────────────┬────────────────────────┘\n                 │\n┌────────────────▼────────────────────────┐\n│      Spring Data OrientDB Layer         │\n├─────────────────────────────────────────┤\n│  - Repository Interfaces                │\n│  - Query Derivation                     │\n│  - Transaction Management               │\n│  - Entity Callbacks                     │\n└────────────────┬────────────────────────┘\n                 │\n┌────────────────▼────────────────────────┐\n│         Mapping \u0026 Conversion            │\n├─────────────────────────────────────────┤\n│  - OrientDBMappingContext               │\n│  - OrientDBEntityConverter              │\n│  - Type Conversion                      │\n└────────────────┬────────────────────────┘\n                 │\n┌────────────────▼────────────────────────┐\n│         OrientDB Template               │\n├─────────────────────────────────────────┤\n│  - Query Execution                      │\n│  - Session Management                   │\n│  - Error Translation                    │\n└────────────────┬────────────────────────┘\n                 │\n┌────────────────▼────────────────────────┐\n│         OrientDB Client                 │\n│    (orientdb-core, orientdb-client)     │\n└─────────────────────────────────────────┘\n```\n\n## 📝 Example Application\n\nComplete working example: [spring-data-orientdb-examples](https://github.com/satishbabariya/spring-data-orientdb-examples)\n\n## 🤝 Contributing\n\nContributions are welcome! Please follow these guidelines:\n\n1. Fork the repository\n2. Create a feature branch: `git checkout -b feature/my-feature`\n3. Commit your changes: `git commit -am 'Add new feature'`\n4. Push to the branch: `git push origin feature/my-feature`\n5. Submit a pull request\n\n### Development Setup\n\n```bash\n# Clone repository\ngit clone https://github.com/satishbabariya/spring-data-orientdb.git\ncd spring-data-orientdb\n\n# Build project\nmvn clean install\n\n# Run tests\nmvn test\n\n# Run integration tests\nmvn verify\n```\n\n### Code Style\n- Follow Spring Framework coding conventions\n- Write comprehensive Javadoc for public APIs\n- Include unit tests for new features\n- Maintain test coverage above 70%\n\n## 📄 License\n\nThis project is licensed under the Apache License 2.0 - see the [LICENSE](LICENSE) file for details.\n\n## 🙏 Acknowledgments\n\n- **Spring Data Team**: For the excellent Spring Data Commons framework\n- **OrientDB Team**: For the powerful multi-model database\n- **Spring Data Neo4j**: Inspiration for the repository abstraction design\n- **Community Contributors**: Thank you for your contributions and feedback\n\n## 📞 Support and Contact\n\n- **Issues**: [GitHub Issues](https://github.com/satishbabariya/spring-data-orientdb/issues)\n- **Discussions**: [GitHub Discussions](https://github.com/satishbabariya/spring-data-orientdb/discussions)\n- **Stack Overflow**: Tag questions with `spring-data-orientdb`\n- **Author**: [@satishbabariya](https://github.com/satishbabariya)\n\n## 🗺️ Roadmap\n\n### Version 1.0.0 (Current)\n- ✅ Core CRUD operations\n- ✅ Repository abstraction\n- ✅ Query method derivation\n- ✅ Pagination and sorting\n- ✅ Transaction management\n- ✅ Entity lifecycle callbacks\n- ✅ Auditing support\n- ✅ Schema generation\n- ✅ Projections\n- ✅ Query By Example\n- ✅ Observability \u0026 metrics\n\n### Version 1.1.0 (Planned)\n- ⏳ Spring Boot Starter auto-configuration\n- ⏳ Reactive repository support (ReactiveOrientDBRepository)\n- ⏳ Criteria API / Specifications\n- ⏳ Lazy loading strategies\n- ⏳ Enhanced caching strategies\n- ⏳ Query hints and optimization\n\n### Version 2.0.0 (Future)\n- ⏳ Full reactive support with Project Reactor\n- ⏳ GraphQL integration\n- ⏳ Native GraalVM support\n- ⏳ Advanced graph analytics\n- ⏳ Multi-tenancy support\n\n---\n\n**Made with ❤️ for the Spring and OrientDB communities**\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsatishbabariya%2Fspring-data-orientdb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsatishbabariya%2Fspring-data-orientdb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsatishbabariya%2Fspring-data-orientdb/lists"}