Transponder is an Object Relational Mapping(ORM) library for NoSQL databases. It's lightweight, with very small memory footprint. Transponder dynamically generates bytecode over native DB client classes, so there are NO overheads for reflection and NO double storing of data.
- Use Cases
- Key Benefits
- NoSQL Database Support Status
- Java Version Requirements
- Getting Started
- Defining DataModel
- Transponder API
- Transponder Annotations
- Support of Multiple Dialects
- Comparison With Other ORMs
- Suppport
Transponder can be used for
- Defining a data model in Java source code
- Automatic creation of a datamodel in a NoSQL DB
- Generation of Data Access Objects (DAO) - utility classes to work with data
- Easy customization for specific needs, for example: introduce custom annotation
@Sudo
to execute code under priviledged access
- Lightweight
- Small memory footprint
- Transferable: datamodel defined for one DB can be reused for another one
- Small learning curve
Database | Status | Type | Complexity | Java API Quality | Community | Performance | Maven Dependency |
---|---|---|---|---|---|---|---|
Currently Supported | |||||||
OrientDB | ✅ | Multi-Model | Medium | Excellent | Strong | High | org.orienteer.transponder:transponder-orientdb |
ArcadeDB | ✅ | Multi-Model | Medium | Good | Growing | High | org.orienteer.transponder:transponder-arcadedb |
Neo4j | ✅ | Graph | Medium | Excellent | Very Strong | High | org.orienteer.transponder:transponder-neo4j |
JanusGraph | ✅ | Graph | Low | Excellent | Strong | High | org.orienteer.transponder:transponder-janusgraph |
MongoDB | ✅ | Document | Medium | Good | Very Strong | High | org.orienteer.transponder:transponder-mongodb |
High Priority - Recommended Next | |||||||
ArangoDB | 🔄 | Multi-Model | Medium | Good | Very Strong | High | org.orienteer.transponder:transponder-arangodb |
CouchDB | 🔄 | Document | Low | Good | Strong | Medium | org.orienteer.transponder:transponder-couchdb |
Medium Priority - Strategic Extensions | |||||||
Redis | 📋 | Key-Value | Low | Excellent | Very Strong | Very High | org.orienteer.transponder:transponder-redis |
Memgraph | 📋 | Graph | Very Low | Excellent | Growing | Very High | org.orienteer.transponder:transponder-memgraph |
Lower Priority - Future Consideration | |||||||
Apache Cassandra | 📋 | Wide Column | High | Excellent | Very Strong | High | org.orienteer.transponder:transponder-cassandra |
Couchbase | 📋 | Document | Medium | Good | Strong | High | org.orienteer.transponder:transponder-couchbase |
Hazelcast | 📋 | Key-Value/Cache | Medium | Excellent | Strong | Very High | org.orienteer.transponder:transponder-hazelcast |
DynamoDB | 📋 | Key-Value/Document | High | Good | Strong | High | org.orienteer.transponder:transponder-dynamodb |
Legend:
- ✅ Currently Supported - Ready to use
- 🔄 High Priority - Recommended for next implementation based on similarity to existing drivers and ecosystem fit
- 📋 Planned - Future consideration based on community demand and strategic value
Please create an issue or discussion if you need support of some other DBs or expedite priority for those which are not yet supported.
Important: Different Transponder modules have specific Java version requirements based on their underlying database dependencies:
- transponder-core: Java 8+ (tested up to Java 21)
- transponder-orientdb: Java 8+ (OrientDB 3.2.36 works with Java 8-21)
- transponder-arcadedb: Java 11+ (ArcadeDB 23.12.1 requirement, works with Java 17+)
- transponder-neo4j: Java 8-17 (Neo4j 4.4.38 fails with Java 21)
- transponder-janusgraph: Java 8-11 (JanusGraph 1.1.0 limitation - requires explicit Java 11 compiler config)
- transponder-mongodb: Java 8+ (MongoDB Java Driver 5.2.1 works with all Java versions)
Module | Java 8 | Java 11 | Java 17 | Java 21 | Notes |
---|---|---|---|---|---|
transponder-core | ✅ | ✅ | ✅ | *Requires Maven compiler config changes for Java 8 | |
transponder-orientdb | ✅ | ✅ | ✅ | *Requires Maven compiler config changes for Java 8 | |
transponder-arcadedb | ❌ | ✅ | ✅ | ✅ | Minimum Java 11 required |
transponder-neo4j | ✅ | ✅ | ❌ | *Requires Maven compiler config changes for Java 8; Fails on Java 21 | |
transponder-janusgraph | ✅ | ❌ | ❌ | *Requires Maven compiler config changes for Java 8; Uses Java 11 compiler config | |
transponder-mongodb | ✅ | ✅ | ✅ | *Requires Maven compiler config changes for Java 8 |
Use Java 11 - all modules work perfectly with Java 11.
Use Java 17 with modules: core, orientdb, arcadedb, mongodb (excludes neo4j and janusgraph).
- Java 11: Safest choice, all modules tested and working
- Java 17: Good choice if not using Neo4j or JanusGraph
- Java 21: Limited to core, orientdb, arcadedb, mongodb modules only
The JanusGraph module provides enterprise-grade distributed graph database capabilities that scale beyond Neo4j limitations:
- Massive Scalability: Handle graphs with billions of vertices and edges across multiple machines
- Multiple Storage Backends: Choose from Cassandra, HBase, or BerkeleyDB for storage
- Native TinkerPop Integration: Full Apache TinkerPop 3.7.3 and Gremlin query support
- ACID Transactions: Complete transactional support for data consistency
- High Availability: Built-in replication and fault tolerance
Use JanusGraph when you need distributed graph processing or when your graph data exceeds single-machine capabilities.
For development environments with multiple Java versions, consider using tools like jenv
or SDKMAN!
to manage Java versions per project.
Before you start: Check the Java Version Requirements section above to ensure your environment meets the requirements for your target database.
Add the following dependency into your pom.xml
:
<dependency>
<groupId>org.orienteer.transponder</groupId>
<artifactId>transponder-${NOSQL DB NAME}</artifactId>
<version>${project.version}</version>
</dependency>
If you are using SNAPSHOT
version, please make sure that the following repository is included into your pom.xml
:
<repository>
<id>Sonatype Nexus</id>
<url>https://oss.sonatype.org/content/repositories/snapshots/</url>
<releases>
<enabled>false</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
Use interface class with annotation @EntityType("EntryTypeName")
to define a type from your data-model. All getters and setters will be mapped to corresponding properties/columns of an entity in a database. You can use interface default methods for your custom methods. There is a set of annotations supported by Transponder to simplify work with the data model: @EntityIndex
, @Query
, @Lookup
, @Command
and etc. Please see corresponding chapter for details. Lets create simple datamodel to mimic file-system:
@EntityType("Entry")
@EntityIndex(name = "nameParent", properties = {"name", "parent"})
public interface IEntry {
public String getName();
public void setName(String value);
public IFolder getParent();
public void setParent(IFolder value);
@Lookup("select from Entry where name=:name and parent=:parent")
public boolean lookupByName(String name, IFolder parent);
public default String getFullPath() {
IFolder parent = getParent();
return (parent!=null?parent.getFullPath():"")+"/"+getName();
}
}
@EntityType("Folder")
public interface IFolder extends IEntry {
public List<IEntry> getChild();
public void setChild(List<IEntry> value);
}
@EntityType("File")
public interface IFile extends IEntry {
public byte[] getContent();
public void setContent(byte[] value);
}
After calling transponder.define(IFile.class, IFolder.class)
Transponder will create the following datamodel in a database:
Additionally you can create Data Access Object to be able to query/modify your data:
public interface IFileSystem {
@Query("select from Folder where name = :name and parent is null")
public IFolder getRoot(String name);
@Query("select from Entry where name=:name and parent=:parent")
public IEntry lookupByName(String name, IFolder parent);
@Query("select from Entry where name like :search")
public List<IEntry> search(String search);
@Command("delete from File where content is null")
public void removeEmpty();
}
To create Transponder instance:
Transponder transponder = new Transponder(driver);
//For example:
Transponder transponder = new Transponder(new ODriver()); //OrientDB
Transponder transponder = new Transponder(new ArcadeDBDriver(arcadeDatabase)); //ArcadeDB
To define datamodel in a database:
transponder.define(IFolder.class, IFile.class, IMyOtherEntity.class, ...);
To create DAO instance from the specified interface or class:
IFileSystem fsDao = transponder.dao(IFileSystem.class);
IFileSystem fsDao = transponder.dao(IFileSystem.class, IOtherDAOClass.class, IYetAnotherDAOClass.class, ...);
After DAO creation you can use it right away:
List<IEntry> textFiles = fsDao.search("%.txt");
To create new wrapped entity:
IFolder folder = transponder.create(IFolder.class);
IFolder folder = transponder.create(IFolder.class, IMyOtherUsefullWrapper.class, ...);
After creation of an entity you can work with it as usual java object:
folder.setName("My Cool Folder");
folder.setParent(myOtherFolder);
String fullPath = folder.getFullPath();
To persist/save entity into DB:
Tranponder.save(folder);
Or you can do simply folder.save()
if you mixin the following method into your wrapper:
public default IEntry save() {
Transponder.save();
return this;
}
Also you can wrap some existing entity from a database into wrapped one. Example for OrientDB:
ODocument myFolderDoc = ...;
IFolder folder = transponder.provide(myFolderDoc, IFolder.class);
IFolder folder = transponder.provide(myFolderDoc, IFolder.class, IMyOtherUsefulWrapper.class, ...); //To mixin other useful interfaces
IFodler folder = transponder.wrap(myFolderDoc); //More generic version, but corresponding wrapper should be defined by transponder.define(...) in this case
If needed, you can unwrap entity as well. Example for OrientDB:
ODocument myFolderDoc = (ODocument)Transponder.unwrap(folder);
Sometimes it's useful to rewrap the same entity but into different class.
MyNewWrapper newWrapper = Transponder.rewrap(oldWrapper, MyNewWrapper.class, SomeOtherInterface.class, ...);
Also you can upgrade existing wrapper by adding more interfaces to be supported
MyEntity myEntity = ...;
myEntity = Transponder.upgrade(myEntity, SomeNewInterface.class, SomeOtherInterface.class);
If you have wrapped entity and you need obtain Transponder:
Transponder transponder = Transponder.getTransponder(folder);
Annotation | Description |
---|---|
@EntityType |
Current class defines entity type |
@EntityIndex/@EntityIndexes |
Creates indexes on the entity type in a database |
@EntityProperty |
This getter/setter method should be mapped to corresponding property of an entity in a database |
@EntityPropertyIndex |
Creates index in a datasource on current property |
@Query |
Method execute query in a database and return corresponding result |
@Lookup |
Lookup database for an entity according to search criterias and if found: replace underling entity of the current wrapper |
@Command |
Execute some command in a database |
@DefaultValue |
Return provided default value if actual result from this method is null |
@OrientDBProperty |
OrientDB specific additional settings for the property |
@ArcadeDBProperty |
ArcadeDB specific additional settings for the property |
Annotations in bytecode generation within Transponder is very flexible (due to Byte Buddy) and can be easily extended to support custom cases. For example: @Sudo
- to execute some method under super user, @Count
- to count number of invokations for metrics and etc.
Queries and commands for the same functions might vary for different databases. Valid SQL for one NoSQL database, might require correction for another one. That's why Transponder supports polyglot definitions for @Query
, @Lookup
and @Command
. Transponder do translation to corresponding dialect during dynamic generation of a wrapper, so there is no overheads during actual runtime. Every query/command has id. It's either can be defined manually (for example @Query(id="myQuery", value="select ...")
) or generated automatically (for example first query for IFileSystem
above will have id <fullpackagename>.IFileSystem.getRoot
. Then Transponder uses provided resource file by path /META-INF/transponder/polyglot.properties
to lookup proper query for a specific dialect. For example, for query with id myQuery
for OrientDB library will look for keys orientdb.myQuery
and orientdb.myQuery.language
. If first one is found - it will be used as actual query for OrientDB. If second one was also found: correspinding language will overload language defined in actual annotation.
Transponder | Spring Data | Hibernate | MyBatis | |
---|---|---|---|---|
NoSQL Support | ✅ | ✅ | ✅ | ❌ |
Universal DataModel Markup | ✅ | ❌ | ❌ | ❌ |
No Double Caching | ✅ | ❌ | ❌ | ❌ |
Easy Extendability On User Level Code | ✅ | ❌ | ❌ | ❌ |
Easy SPI For New Drivers | ✅ | ❌ | ❌ | ❌ |
Support of Multi-Inheritance | ✅ | ❌ | ❌ | ❌ |
Disclaimer: Comparison might be subjective. If you have comments: please create an issue or discussion
If you need any support or questions please create an issue or discussion.