183 lines
4.2 KiB
Markdown
183 lines
4.2 KiB
Markdown
# Simple Persistence for Kotlin
|
|
|
|
A lightweight, file-based persistence library for Kotlin applications that provides a simple way to store, retrieve, and query objects.
|
|
|
|
## Available on Maven Central
|
|
|
|
```kotlin
|
|
// Gradle Kotlin DSL
|
|
implementation("nl.astraeus:simple-persistence-kotlin:1.1.4")
|
|
```
|
|
|
|
```groovy
|
|
// Gradle Groovy DSL
|
|
implementation 'nl.astraeus:simple-persistence-kotlin:1.1.4'
|
|
```
|
|
|
|
```xml
|
|
<!-- Maven -->
|
|
<dependency>
|
|
<groupId>nl.astraeus</groupId>
|
|
<artifactId>simple-persistence-kotlin</artifactId>
|
|
<version>1.1.4</version>
|
|
</dependency>
|
|
```
|
|
|
|
## Features
|
|
|
|
- Simple and intuitive API for persisting Kotlin objects
|
|
- Transaction support with ACID properties
|
|
- Optimistic locking to handle concurrent modifications
|
|
- Flexible indexing for efficient querying
|
|
- Object references and collections handling
|
|
- Encryption support for sensitive data
|
|
- Snapshot and transaction log capabilities
|
|
- File-based storage with automatic cleanup
|
|
|
|
## Quick Start
|
|
|
|
### Define your model
|
|
|
|
```kotlin
|
|
class Person(
|
|
override var id: Long = 0,
|
|
override var version: Long = 0,
|
|
val name: String,
|
|
val age: Int,
|
|
company: Company? = null
|
|
) : Persistable, Cloneable {
|
|
var company: Company? by nullableReference(company)
|
|
|
|
companion object {
|
|
private const val serialVersionUID: Long = 1L
|
|
}
|
|
}
|
|
|
|
class Company(
|
|
override var id: Long = 0,
|
|
override var version: Long = 0,
|
|
val name: String
|
|
) : Persistable, Cloneable {
|
|
val persons: MutableCollection<Person> = referenceCollection()
|
|
|
|
companion object {
|
|
private const val serialVersionUID: Long = 1L
|
|
}
|
|
}
|
|
```
|
|
|
|
### Initialize the persistence manager
|
|
|
|
```kotlin
|
|
val persistent = Persistent(
|
|
directory = File("data", "my-app"),
|
|
indexes = arrayOf(
|
|
index<Person>("name") { p -> (p as? Person)?.name ?: "" },
|
|
index<Person>("age") { p -> (p as? Person)?.age ?: -1 },
|
|
index<Company>("name") { p -> (p as? Company)?.name ?: "" }
|
|
)
|
|
)
|
|
```
|
|
|
|
### Perform CRUD operations
|
|
|
|
```kotlin
|
|
// Create or update
|
|
persistent.transaction {
|
|
val person = Person(
|
|
id = 1L,
|
|
name = "John Doe",
|
|
age = 30
|
|
)
|
|
|
|
val company = Company(
|
|
id = 1L,
|
|
name = "ACME Inc."
|
|
)
|
|
|
|
person.company = company
|
|
company.persons.add(person)
|
|
|
|
store(person)
|
|
store(company)
|
|
}
|
|
|
|
// Read
|
|
persistent.query {
|
|
val person = find<Person>(1L)
|
|
println("Found person: $person")
|
|
|
|
// Query by index
|
|
val persons: List<Person> = findByIndex("age", 30)
|
|
|
|
// Search with predicate
|
|
val results = search(Person::class) { p -> p.name.startsWith("John") }
|
|
}
|
|
|
|
// Delete
|
|
persistent.transaction {
|
|
val person = find<Person>(1L)
|
|
if (person != null) {
|
|
delete(person)
|
|
}
|
|
}
|
|
```
|
|
|
|
## How it works
|
|
|
|
- In-memory model: Your objects live in memory during normal operation for fast reads and writes.
|
|
- Durable writes: Every create/update/delete inside a transaction is appended to on-disk transaction log files (*.trn). This is a write-ahead log approach.
|
|
- Startup recovery: On application startup, the library replays the transaction logs (and any snapshot, if present) to rebuild the in-memory model.
|
|
- Snapshots: You can create a compact on-disk snapshot (*.snp) of the current in-memory state. On the next startup, the snapshot is loaded first and only the newer transaction log files are replayed. This keeps startup time low without losing durability.
|
|
|
|
Tip: Call snapshot() periodically (or on controlled shutdown) and use removeOldFiles() to prune obsolete logs after a successful snapshot.
|
|
|
|
## Advanced Features
|
|
|
|
### Indexing
|
|
|
|
```kotlin
|
|
// Define complex indexes
|
|
val persistent = Persistent(
|
|
directory = File("data", "my-app"),
|
|
indexes = arrayOf(
|
|
index<Person>("ageOver30") { p -> ((p as? Person)?.age ?: 0) > 30 }
|
|
)
|
|
)
|
|
|
|
// Query using indexes
|
|
persistent.query {
|
|
findByIndex<Person>("ageOver30", true).forEach { person ->
|
|
println("Person over 30: ${person.name}")
|
|
}
|
|
}
|
|
```
|
|
|
|
### Transactions
|
|
|
|
```kotlin
|
|
// Read-only query
|
|
persistent.query {
|
|
// Only read operations allowed
|
|
}
|
|
|
|
// Read-write transaction
|
|
persistent.transaction {
|
|
// Both read and write operations allowed
|
|
}
|
|
```
|
|
|
|
### Snapshots and Maintenance
|
|
|
|
```kotlin
|
|
// Create a snapshot of the current state
|
|
persistent.snapshot()
|
|
|
|
// Remove old transaction log files
|
|
persistent.removeOldFiles()
|
|
```
|
|
|
|
## License
|
|
|
|
This project is licensed under the MIT License - see the LICENSE file for details.
|