NOsql Object (NO2 a.k.a Nitrite) database is an open source nosql embedded document store. It supports both in-memory and file based persistent store.
Nitrite is an embedded database ideal for desktop, mobile or small web applications.
It features:
- Embedded, serverless
- Simple API
- Document-oriented
- Schemaless document collection and object repository
- Extensible storage engines - mvstore, rocksdb
- Indexing and full-text search
- Simple query api
- In-memory and file-based store
- Transaction support
- Schema migration support
- Encryption support
- Android compatibility (API Level 24)
Nitrite has a kotlin extension called Potassium Nitrite for kotlin developers. Visit here for more details.
If you are looking for Nitrite for Flutter/Dart, head over to nitrite-flutter.
Nitrite DataGate and Nitrite Explorer is now deprecated and no longer maintained.
NOTE: There are breaking api changes in version 4.x. So please read the guide before upgrading from 3.x.x.
To use Nitrite in any Java application, first add the nitrite bill of materials, then add required dependencies:
Maven
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.dizitart</groupId>
<artifactId>nitrite-bom</artifactId>
<version>[latest version]</version>
<scope>import</scope>
<type>pom</type>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.dizitart</groupId>
<artifactId>nitrite</artifactId>
</dependency>
<dependency>
<groupId>org.dizitart</groupId>
<artifactId>nitrite-mvstore-adapter</artifactId>
</dependency>
</dependencies>
Gradle
implementation(platform("org.dizitart:nitrite-bom:[latest version]"))
implementation 'org.dizitart:nitrite'
implementation 'org.dizitart:nitrite-mvstore-adapter'
A Todo android application is available here to demonstrate the usage of Nitrite in android.
Initialize Database
// create a mvstore backed storage module
MVStoreModule storeModule = MVStoreModule.withConfig()
.filePath("/tmp/test.db")
.compress(true)
.build();
// or a rocksdb based storage module
RocksDBModule storeModule = RocksDBModule.withConfig()
.filePath("/tmp/test.db")
.build();
// initialization using builder
Nitrite db = Nitrite.builder()
.loadModule(storeModule)
.loadModule(new JacksonMapperModule()) // optional
.openOrCreate("user", "password");
Create a Collection
// Create a Nitrite Collection
NitriteCollection collection = db.getCollection("test");
// Create an Object Repository
ObjectRepository<Employee> repository = db.getRepository(Employee.class);
Annotations for POJO
@Entity(value = "retired-employee", // entity name (optional),
indices = {
@Index(value = "firstName", type = IndexType.NON_UNIQUE),
@Index(value = "lastName", type = IndexType.NON_UNIQUE),
@Index(value = "note", type = IndexType.FULL_TEXT),
})
public class Employee implements Serializable {
// provides id field to uniquely identify an object inside an ObjectRepository
@Id
private long empId;
private Date joinDate;
private String firstName;
private String lastName;
private String note;
// ... public getters and setters
}
CRUD Operations
// create a document to populate data
Document doc = Document.createDocument("firstName", "John")
.put("lastName", "Doe")
.put("birthDay", new Date())
.put("data", new byte[] {1, 2, 3})
.put("fruits", new ArrayList<String>() {{ add("apple"); add("orange"); add("banana"); }})
.put("note", "a quick brown fox jump over the lazy dog");
// insert the document
collection.insert(doc);
// find a document
collection.find(where("firstName").eq("John").and(where("lastName").eq("Doe"));
// update the document
collection.update(where("firstName").eq("John"), createDocument("lastName", "Wick"));
// remove the document
collection.remove(doc);
// insert an object in repository
Employee emp = new Employee();
emp.setEmpId(124589);
emp.setFirstName("John");
emp.setLastName("Doe");
repository.insert(emp);
Create Indices
// create document index
collection.createIndex(indexOptions(IndexType.NON_UNIQUE), "firstName", "lastName"); // compound index
collection.createIndex(indexOptions(IndexType.FULL_TEXT), "note"); // full-text index
// create object index. It can also be provided via annotation
repository.createIndex(indexOptions(IndexType.NON_UNIQUE), "firstName");
Query a Collection
DocumentCursor cursor = collection.find(
where("firstName").eq("John") // firstName == John
.and(
where("data").elemMatch("$".lt(4)) // AND elements of data array is less than 4
.and(
where("note").text("quick") // AND note field contains string 'quick' using full-text index
)
)
);
for (Document document : cursor) {
// process the document
}
// get document by id
Document document = collection.getById(nitriteId);
// query an object repository and create the first result
Cursor<Employee> cursor = repository.find(where("firstName").eq("John"));
Employee employee = cursor.firstOrNull();
Transaction
try (Session session = db.createSession()) {
try (Transaction transaction = session.beginTransaction()) {
NitriteCollection txCol = transaction.getCollection("test");
Document document = createDocument("firstName", "John");
txCol.insert(document);
transaction.commit();
} catch (TransactionException e) {
transaction.rollback();
}
}
Schema Migration
Migration migration1 = new Migration(Constants.INITIAL_SCHEMA_VERSION, 2) {
@Override
public void migrate(InstructionSet instructions) {
instructions.forDatabase()
// make a non-secure db to secure db
.addUser("test-user", "test-password");
// create instructions for existing repository
instructions.forRepository(OldClass.class, "demo1")
// rename the repository (in case of entity name changes)
.renameRepository("migrated", null)
// change datatype of field empId from String to Long and convert the values
.changeDataType("empId", (TypeConverter<String, Long>) Long::parseLong)
// change id field from uuid to empId
.changeIdField(Fields.withNames("uuid"), Fields.withNames("empId"))
// delete uuid field
.deleteField("uuid")
// rename field from lastName to familyName
.renameField("lastName", "familyName")
// add new field fullName and add default value as - firstName + " " + lastName
.addField("fullName", document -> document.get("firstName", String.class) + " "
+ document.get("familyName", String.class))
// drop index on firstName
.dropIndex("firstName")
// drop index on embedded field literature.text
.dropIndex("literature.text")
// change data type of embedded field from float to integer and convert the values
.changeDataType("literature.ratings", (TypeConverter<Float, Integer>) Math::round);
}
};
Migration migration2 = new Migration(2, 3) {
@Override
public void migrate(InstructionSet instructions) {
instructions.forCollection("test")
.addField("fullName", "Dummy Name");
}
};
MVStoreModule storeModule = MVStoreModule.withConfig()
.filePath("/temp/employee.db")
.compressHigh(true)
.build();
db = Nitrite.builder()
.loadModule(storeModule)
// schema versioning is must for migration
.schemaVersion(2)
// add defined migration paths
.addMigrations(migration1, migration2)
.openOrCreate();
Import/Export Data
// Export data to json file
// create export options
ExportOptions exportOptions = new ExportOptions();
// set the nitrite factory
exportOptions.setNitriteFactory(() -> openDb("test.db"));
// set the collections to export
exportOptions.setCollections(List.of("first"));
// set the repositories to export
exportOptions.setRepositories(List.of("org.dizitart.no2.support.data.Employee", "org.dizitart.no2.support.data.Company"));
// set the keyed repositories to export
exportOptions.setKeyedRepositories(Map.of("key", Set.of("org.dizitart.no2.support.data.Employee")));
// create an exporter with export options
Exporter exporter = Exporter.withOptions(exportOptions);
exporter.exportTo("test.json");
// Import data from the file
// create import options
ImportOptions importOptions = new ImportOptions();
// set the nitrite factory
importOptions.setNitriteFactory(() -> openDb("new-test.db"));
// create an importer with import options
Importer importer = Importer.withOptions(importOptions);
importer.importFrom("test.json");
More details are available in the guide.
Release notes are available here.
Reference | API |
---|---|
To build and test Nitrite, ensure you have JDK 11 (or higher) and Maven 3 installed.
git clone https://github.com/nitrite/nitrite-java.git
cd nitrite-java
mvn clean install
For issues with, questions about, or feedback create a discussion.
Think you’ve found a bug? Want to see a new feature in the Nitrite? Please open an issue here. But before you file an issue please check if it is already existing or not.
- Anindya Chatterjee
This project exists thanks to all the people who contribute. For more details please visit CONTRIBUTING.md.
Support this project by becoming a sponsor. Your logo will show up here with a link to your website. Become a sponsor for this project.
Idan Sheinberg has given a talk on Nitrite at Kotlin Everywhere - TLV Edition meetup on October 27, 2019. Please find his presentation here.