⚠️ MIGRATION NOTICE The official repository for JSNDB has moved from SourceForge to GitHub! This is now the main repository for future development, issues, and releases.
JSNDB is a fully embedded, 100% serverless NoSQL Document Database written purely in Java. It allows you to persist, query, update, and delete Java objects (POJOs) natively to disk using a lightweight JSON structure combined with an extremely optimized binary B-Tree indexing engine.
It eliminates the need for strict schemas, ORMs, and heavy connection protocols by working directly with your memory objects.
- No Dependencies, No Servers: Runs inside your JVM.
- POJO Native: Save your Java Objects directly. No mappers or DTO conversions needed.
- Blazing Fast Binary Indices: Properties are mapped into a binary B-Tree that avoids parsing JSON during operations.
- Circular Reference Support: Handles cyclic relationships (e.g.
Owner -> Pet -> Owner) gracefully without StackOverflow crashes, using lazy loading proxies. - Modern & Lightweight: Compatible with Java 17 / Java 21 LTS.
Just use your normal Java objects. Use @jsndbObjectId to mark the ID.
public class Persona {
@jsndbObjectId
private Long id;
private String name;
private int age;
private List<Animal> pets;
// constructors, getters, setters...
}// Open or create the database directory
jsndb db = new jsndb("my_database_dir");
// Create your complex nested objects
Persona user = new Persona("Alice", 28, new ArrayList<>());
Animal dog = new Animal("Buddy", "Golden Retriever", user);
user.getPets().add(dog);
// Persist the objects natively (JSNDB handles the relations automatically!)
db.persist(dog);
db.persist(user);
db.commit(); // Writes gracefully to disk// Builder for searching
qwery q = qwery.create(Persona.class, "Alice", "name", comparators.equal);
List<Persona> results = db.select(Persona.class, q);
for (Persona p : results) {
System.out.println("Found " + p.getName() + " with " + p.getPets().size() + " pets!");
}JSNDB was heavily optimized and benchmarked directly against industry leviathans: SQLite (C-based relational DB) and Nitrite (Java embedded NoSQL DB).
The test involved inserting, selecting, and modifying 400,000 extreme relational objects (100,000 Persons each containing 3 nested Pets).
| Operation | JSNDB (NoSQL) | Nitrite Database (NoSQL) | SQLite (RDBMS) |
|---|---|---|---|
| Insert | 4.566 ms | 10.259 ms | 2.073 ms |
| Select (Indexed) | 2.165 ms | 7.683 ms | 369 ms |
| Update (Bulk) | 13 ms 🔥 | 10.436 ms | 104 ms |
| Delete (Bulk) | 0 ms (Instant) 🔥 | 12.790 ms | 13 ms |
- Obliterates NoSQL Competition: JSNDB performs inserts 2.2x faster and selects 3.5x faster than Nitrite Database.
- Bulk Dominance: In mass modifications (Updates and Deletes), JSNDB resolves logical operations seamlessly in under 15 milliseconds, beating even the raw C engine of SQLite (which took ~100ms) and destroying Nitrite which took more than 10-12 seconds.
- Unhackable Circularity: Nitrite crashed (
StackOverflowError) during the test due to cyclic POJO references via Jackson. JSNDB'slazyloadproxy matrix resolved the references natively without requiring dirty@JsonIgnoredecorators.
// 1. Insert 100k Entities (with relations)
for (int i = 0; i < COUNT; i++) {
persona p = new persona("User " + i, i, new ArrayList<>());
for (int k=0; k < 3; k++) {
animal a = new animal("Pet " + k, "Breed", p);
p.getMascota().add(a);
js.persist(a);
}
js.persist(p);
}
js.commit();
// 2. Select by Property (Indexed)
qwery q = qwery.create(persona.class, "User 500", "name", comparators.equal);
List<persona> results = js.select(persona.class, q);// 1. Relational Table Setup & Inserts
st.execute("CREATE TABLE persona (id INTEGER PRIMARY KEY, name TEXT, age INTEGER)");
st.execute("CREATE TABLE animal (id INTEGER PRIMARY KEY, nombre TEXT, owner_id INTEGER)");
conn.setAutoCommit(false);
for (int i = 0; i < COUNT; i++) {
psPersona.setString(2, "User " + i);
psPersona.executeUpdate();
for (int k=0; k < 3; k++) {
psAnimal.setString(1, "Pet " + k);
psAnimal.setInt(2, i);
psAnimal.executeUpdate();
}
}
conn.commit();// 1. Repository Setup & Indexing
ObjectRepository<persona> repo = db.getRepository(persona.class);
repo.createIndex("name", IndexOptions.indexOptions(IndexType.NonUnique));
for (int i = 0; i < COUNT; i++) {
persona p = new persona("User " + i, i, new ArrayList<>());
repo.insert(p); // Nitrite crashes on circular references
}
db.commit();Download the jsndb-beta1.jar from the release section to try it directly in your project.
javac -d bin src/**/*.java
jar cvf jsndb-beta1.jar -C bin/ .
