# 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 nl.astraeus simple-persistence-kotlin 1.1.4 ``` ## 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 = 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("name") { p -> (p as? Person)?.name ?: "" }, index("age") { p -> (p as? Person)?.age ?: -1 }, index("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(1L) println("Found person: $person") // Query by index val persons: List = findByIndex("age", 30) // Search with predicate val results = search(Person::class) { p -> p.name.startsWith("John") } } // Delete persistent.transaction { val person = find(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("ageOver30") { p -> ((p as? Person)?.age ?: 0) > 30 } ) ) // Query using indexes persistent.query { findByIndex("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.